From b8ca193606df9bb27268c547cd3d90bbf8b52513 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 31 Jul 2023 11:21:22 +0930 Subject: [PATCH] renepay: an experimental payment plugin Signed-off-by: Lagrang3 Changelog-Added: Plugins: `renepay`: an experimental pay plugin implementing Pickhardt payments (`renepay` and `renepaystatus`). --- plugins/Makefile | 2 + plugins/renepay/Makefile | 16 + plugins/renepay/debug.c | 51 + plugins/renepay/debug.h | 54 + plugins/renepay/dijkstra.c | 173 +++ plugins/renepay/dijkstra.h | 49 + plugins/renepay/flow.c | 828 +++++++++++ plugins/renepay/flow.h | 319 +++++ plugins/renepay/heap.h | 195 +++ plugins/renepay/mcf.c | 1507 ++++++++++++++++++++ plugins/renepay/mcf.h | 71 + plugins/renepay/pay.c | 1792 ++++++++++++++++++++++++ plugins/renepay/pay.h | 117 ++ plugins/renepay/pay_flow.c | 633 +++++++++ plugins/renepay/pay_flow.h | 112 ++ plugins/renepay/payment.c | 230 +++ plugins/renepay/payment.h | 163 +++ plugins/renepay/test/Makefile | 19 + plugins/renepay/test/run-dijkstra.c | 100 ++ plugins/renepay/test/run-mcf-diamond.c | 156 +++ plugins/renepay/test/run-mcf.c | 470 +++++++ plugins/renepay/test/run-payflow_map.c | 96 ++ plugins/renepay/test/run-testflow.c | 696 +++++++++ plugins/renepay/uncertainty_network.c | 348 +++++ plugins/renepay/uncertainty_network.h | 47 + tests/test_renepay.py | 239 ++++ 26 files changed, 8483 insertions(+) create mode 100644 plugins/renepay/Makefile create mode 100644 plugins/renepay/debug.c create mode 100644 plugins/renepay/debug.h create mode 100644 plugins/renepay/dijkstra.c create mode 100644 plugins/renepay/dijkstra.h create mode 100644 plugins/renepay/flow.c create mode 100644 plugins/renepay/flow.h create mode 100644 plugins/renepay/heap.h create mode 100644 plugins/renepay/mcf.c create mode 100644 plugins/renepay/mcf.h create mode 100644 plugins/renepay/pay.c create mode 100644 plugins/renepay/pay.h create mode 100644 plugins/renepay/pay_flow.c create mode 100644 plugins/renepay/pay_flow.h create mode 100644 plugins/renepay/payment.c create mode 100644 plugins/renepay/payment.h create mode 100644 plugins/renepay/test/Makefile create mode 100644 plugins/renepay/test/run-dijkstra.c create mode 100644 plugins/renepay/test/run-mcf-diamond.c create mode 100644 plugins/renepay/test/run-mcf.c create mode 100644 plugins/renepay/test/run-payflow_map.c create mode 100644 plugins/renepay/test/run-testflow.c create mode 100644 plugins/renepay/uncertainty_network.c create mode 100644 plugins/renepay/uncertainty_network.h create mode 100644 tests/test_renepay.py diff --git a/plugins/Makefile b/plugins/Makefile index 85d9e1d83..09f57fcaf 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -100,6 +100,7 @@ C_PLUGINS := \ plugins/offers \ plugins/pay \ plugins/txprepare \ + plugins/cln-renepay \ plugins/spenderp PY_PLUGINS := \ @@ -177,6 +178,7 @@ PLUGIN_COMMON_OBJS := \ wire/towire.o include plugins/bkpr/Makefile +include plugins/renepay/Makefile # Make sure these depend on everything. ALL_C_SOURCES += $(PLUGIN_ALL_SRC) diff --git a/plugins/renepay/Makefile b/plugins/renepay/Makefile new file mode 100644 index 000000000..e35922b13 --- /dev/null +++ b/plugins/renepay/Makefile @@ -0,0 +1,16 @@ +PLUGIN_RENEPAY_SRC := plugins/renepay/pay.c plugins/renepay/pay_flow.c plugins/renepay/flow.c plugins/renepay/mcf.c plugins/renepay/dijkstra.c \ + plugins/renepay/debug.c plugins/renepay/payment.c plugins/renepay/uncertainty_network.c +PLUGIN_RENEPAY_HDRS := plugins/renepay/pay.h plugins/renepay/pay_flow.h plugins/renepay/flow.h plugins/renepay/mcf.h plugins/renepay/heap.h plugins/renepay/dijkstra.h \ + plugins/renepay/debug.h plugins/renepay/payment.h plugins/renepay/uncertainty_network.h +PLUGIN_RENEPAY_OBJS := $(PLUGIN_RENEPAY_SRC:.c=.o) + +# Make sure these depend on everything. +ALL_C_SOURCES += $(PLUGIN_RENEPAY_SRC) +ALL_C_HEADERS += $(PLUGIN_RENEPAY_HDRS) + +# Make all plugins depend on all plugin headers, for simplicity. +$(PLUGIN_RENEPAY_OBJS): $(PLUGIN_RENEPAY_HDRS) + +plugins/cln-renepay: $(PLUGIN_RENEPAY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) bitcoin/chainparams.o common/gossmap.o common/fp16.o common/dijkstra.o common/bolt12.o common/bolt12_merkle.o wire/bolt12$(EXP)_wiregen.o + +include plugins/renepay/test/Makefile diff --git a/plugins/renepay/debug.c b/plugins/renepay/debug.c new file mode 100644 index 000000000..7394dd5d5 --- /dev/null +++ b/plugins/renepay/debug.c @@ -0,0 +1,51 @@ +#include "config.h" +#include + +void _debug_exec_branch(const char* fname,const char* fun, int lineno) +{ + FILE *f = fopen(fname,"a"); + fprintf(f,"executing line: %d (%s)\n",lineno,fun); + fclose(f); +} + +void _debug_outreq(const char *fname, const struct out_req *req) +{ + FILE *f = fopen(fname,"a"); + size_t len; + const char * str = json_out_contents(req->js->jout,&len); + fprintf(f,"%s",str); + if (req->errcb) + fprintf(f,"}"); + fprintf(f,"}\n"); + fclose(f); +} + +void _debug_call(const char* fname, const char* fun) +{ + FILE *f = fopen(fname,"a"); + fprintf(f,"calling function: %s\n",fun); + fclose(f); +} + +void _debug_reply(const char* fname, const char* buf,const jsmntok_t *toks) +{ + FILE *f = fopen(fname,"a"); + fprintf(f,"%.*s\n\n", + json_tok_full_len(toks), + json_tok_full(buf, toks)); + fclose(f); +} + +void _debug_info(const char* fname, const char *fmt, ...) +{ + FILE *f = fopen(fname,"a"); + + va_list args; + va_start(args, fmt); + + vfprintf(f,fmt,args); + + va_end(args); + fclose(f); +} + diff --git a/plugins/renepay/debug.h b/plugins/renepay/debug.h new file mode 100644 index 000000000..3a1b051e9 --- /dev/null +++ b/plugins/renepay/debug.h @@ -0,0 +1,54 @@ +#ifndef LIGHTNING_PLUGINS_RENEPAY_DEBUG_H +#define LIGHTNING_PLUGINS_RENEPAY_DEBUG_H +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include + +void _debug_outreq(const char *fname, const struct out_req *req); +void _debug_reply(const char* fname, const char* buf,const jsmntok_t *toks); +void _debug_info(const char* fname, const char *fmt, ...); +void _debug_call(const char* fname, const char* fun); +void _debug_exec_branch(const char* fname,const char* fun, int lineno); + +#ifndef MYLOG +#define MYLOG "/tmp/debug.txt" +#endif + + +/* All debug information goes to a file. */ +#ifdef RENEPAY_UNITTEST + +#define debug_info(...) \ + _debug_info(MYLOG,__VA_ARGS__) + +#define debug_err(...) \ + {_debug_info(MYLOG,__VA_ARGS__); abort();} + +#define debug_paynote(p,...) \ + {payment_note(p,__VA_ARGS__);_debug_info(MYLOG,__VA_ARGS__);} + +#else +/* Debugging information goes either to payment notes or to lightningd log. */ + +#define debug_info(...) \ + plugin_log(pay_plugin->plugin,LOG_DBG,__VA_ARGS__) + +#define debug_err(...) \ + plugin_err(pay_plugin->plugin,__VA_ARGS__) + +#define debug_paynote(p,...) \ + payment_note(p,__VA_ARGS__); + +#endif + +#define debug_assert(expr) \ + if(!(expr)) debug_err("Assertion failed %s, file: %s, line %d", #expr,__FILE__,__LINE__) + + +#endif /* LIGHTNING_PLUGINS_RENEPAY_DEBUG_H */ diff --git a/plugins/renepay/dijkstra.c b/plugins/renepay/dijkstra.c new file mode 100644 index 000000000..d8a2135d1 --- /dev/null +++ b/plugins/renepay/dijkstra.c @@ -0,0 +1,173 @@ +#include "config.h" +#include + +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; +} + +/* Destructor for global dijkstra. The valid free state is signalled with a + * NULL ptr. */ +static void dijkstra_destroy(struct dijkstra *ptr UNUSED) +{ + global_dijkstra=NULL; +} + +/* Manually release dijkstra resources. */ +void dijkstra_free(void) +{ + if(global_dijkstra) + { + global_dijkstra = tal_free(global_dijkstra); + } +} + +/* Allocation of resources for the heap. */ +void dijkstra_malloc(const tal_t *ctx, const size_t max_num_nodes) +{ + dijkstra_free(); + + global_dijkstra = tal(ctx,struct dijkstra); + tal_add_destructor(global_dijkstra,dijkstra_destroy); + + global_dijkstra->distance = tal_arr(global_dijkstra,s64,max_num_nodes); + global_dijkstra->base = tal_arr(global_dijkstra,u32,max_num_nodes); + global_dijkstra->heapptr = tal_arrz(global_dijkstra,u32*,max_num_nodes); + + global_dijkstra->heapsize=0; + + global_dijkstra->gheap_ctx.fanout=2; + global_dijkstra->gheap_ctx.page_chunks=1024; + global_dijkstra->gheap_ctx.item_size=sizeof(global_dijkstra->base[0]); + global_dijkstra->gheap_ctx.less_comparer=dijkstra_less_comparer; + global_dijkstra->gheap_ctx.less_comparer_ctx=NULL; + global_dijkstra->gheap_ctx.item_mover=dijkstra_item_mover; +} + + +void dijkstra_init(void) +{ + const size_t max_num_nodes = tal_count(global_dijkstra->distance); + global_dijkstra->heapsize=0; + for(size_t i=0;idistance[i]=INFINITE; + global_dijkstra->heapptr[i] = NULL; + } +} +size_t dijkstra_size(void) +{ + return global_dijkstra->heapsize; +} + +size_t dijkstra_maxsize(void) +{ + return tal_count(global_dijkstra->distance); +} + +static void dijkstra_append(u32 node_idx, s64 distance) +{ + assert(dijkstra_size() < dijkstra_maxsize()); + assert(node_idx < dijkstra_maxsize()); + + const size_t pos = global_dijkstra->heapsize; + + global_dijkstra->base[pos]=node_idx; + global_dijkstra->distance[node_idx]=distance; + global_dijkstra->heapptr[node_idx] = &(global_dijkstra->base[pos]); + global_dijkstra->heapsize++; +} +void dijkstra_update(u32 node_idx, s64 distance) +{ + assert(node_idx < dijkstra_maxsize()); + + if(!global_dijkstra->heapptr[node_idx]) + { + // not in the heap + dijkstra_append(node_idx,distance); + gheap_restore_heap_after_item_increase( + &global_dijkstra->gheap_ctx, + global_dijkstra->base, + global_dijkstra->heapsize, + global_dijkstra->heapptr[node_idx] + - global_dijkstra->base); + return; + } + + if(global_dijkstra->distance[node_idx] > distance) + { + // distance decrease + global_dijkstra->distance[node_idx] = distance; + + gheap_restore_heap_after_item_increase( + &global_dijkstra->gheap_ctx, + global_dijkstra->base, + global_dijkstra->heapsize, + global_dijkstra->heapptr[node_idx] + - global_dijkstra->base); + }else + { + // distance increase + global_dijkstra->distance[node_idx] = distance; + + gheap_restore_heap_after_item_decrease( + &global_dijkstra->gheap_ctx, + global_dijkstra->base, + global_dijkstra->heapsize, + global_dijkstra->heapptr[node_idx] + - global_dijkstra->base); + + } + // assert(gheap_is_heap(&global_dijkstra->gheap_ctx, + // global_dijkstra->base, + // dijkstra_size())); +} +u32 dijkstra_top(void) +{ + return global_dijkstra->base[0]; +} +bool dijkstra_empty(void) +{ + return global_dijkstra->heapsize==0; +} +void dijkstra_pop(void) +{ + if(global_dijkstra->heapsize==0) + return; + + const u32 top = dijkstra_top(); + assert(global_dijkstra->heapptr[top]==global_dijkstra->base); + + gheap_pop_heap( + &global_dijkstra->gheap_ctx, + global_dijkstra->base, + global_dijkstra->heapsize--); + + global_dijkstra->heapptr[top]=NULL; +} +const s64* dijkstra_distance_data(void) +{ + return global_dijkstra->distance; +} diff --git a/plugins/renepay/dijkstra.h b/plugins/renepay/dijkstra.h new file mode 100644 index 000000000..7321e5258 --- /dev/null +++ b/plugins/renepay/dijkstra.h @@ -0,0 +1,49 @@ +#ifndef LIGHTNING_PLUGINS_RENEPAY_DIJKSTRA_H +#define LIGHTNING_PLUGINS_RENEPAY_DIJKSTRA_H +#include "config.h" +#include +#include +#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; +}; + +/* Allocation of resources for the heap. */ +void dijkstra_malloc(const tal_t *ctx, const size_t max_num_nodes); + +/* Manually release dijkstra resources. */ +void dijkstra_free(void); + +/* Initialization of the heap for a new Dijkstra search. */ +void dijkstra_init(void); + +/* Inserts a new element in the heap. If node_idx was already in the heap then + * its distance value is updated. */ +void dijkstra_update(u32 node_idx, s64 distance); + +u32 dijkstra_top(void); +bool dijkstra_empty(void); +void dijkstra_pop(void); + +const s64* dijkstra_distance_data(void); + +/* Number of elements on the heap. */ +size_t dijkstra_size(void); + +/* Maximum number of elements the heap can host */ +size_t dijkstra_maxsize(void); + +#endif /* LIGHTNING_PLUGINS_RENEPAY_DIJKSTRA_H */ diff --git a/plugins/renepay/flow.c b/plugins/renepay/flow.c new file mode 100644 index 000000000..bdeca09d3 --- /dev/null +++ b/plugins/renepay/flow.c @@ -0,0 +1,828 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SUPERVERBOSE +#define SUPERVERBOSE(...) +#else +#define SUPERVERBOSE_ENABLED 1 +#endif + +bool chan_extra_is_busy(struct chan_extra const * 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 = + type_to_string(this_ctx,struct short_channel_id,&ch->scid); + for(int dir=0;dir<2;++dir) + { + tal_append_fmt(&buff,"%s[%d]:(%s,%s)\n",scid_str,dir, + type_to_string(this_ctx,struct amount_msat,&ch->half[dir].known_min), + type_to_string(this_ctx,struct amount_msat,&ch->half[dir].known_max)); + } + } + tal_free(this_ctx); + return buff; +} + +struct chan_extra *new_chan_extra( + struct chan_extra_map *chan_extra_map, + const struct short_channel_id scid, + struct amount_msat capacity) +{ + struct chan_extra *ce = tal(chan_extra_map, struct chan_extra); + + 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; + } + chan_extra_map_add(chan_extra_map, 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; +} + +/* This helper function preserves the uncertainty network invariant after the + * knowledge is updated. It assumes that the (channel,!dir) knowledge is + * correct. */ +void chan_extra_adjust_half(struct chan_extra *ce, + int dir) +{ + if(!amount_msat_sub(&ce->half[dir].known_max,ce->capacity,ce->half[!dir].known_min)) + { + debug_err("%s cannot substract capacity=%s and known_min=%s", + __PRETTY_FUNCTION__, + type_to_string(tmpctx,struct amount_msat,&ce->capacity), + type_to_string(tmpctx,struct amount_msat,&ce->half[!dir].known_min) + ); + } + if(!amount_msat_sub(&ce->half[dir].known_min,ce->capacity,ce->half[!dir].known_max)) + { + debug_err("%s cannot substract capacity=%s and known_max=%s", + __PRETTY_FUNCTION__, + type_to_string(tmpctx,struct amount_msat,&ce->capacity), + type_to_string(tmpctx,struct amount_msat,&ce->half[!dir].known_max) + ); + } +} + + +/* Update the knowledge that this (channel,direction) can send x msat.*/ +static void chan_extra_can_send_( + struct chan_extra *ce, + int dir, + struct amount_msat x) +{ + if(amount_msat_greater(x,ce->capacity)) + { + debug_err("%s unexpected capacity=%s is less than x=%s", + __PRETTY_FUNCTION__, + type_to_string(tmpctx,struct amount_msat,&ce->capacity), + type_to_string(tmpctx,struct amount_msat,&x) + ); + x = ce->capacity; + } + + 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); + + chan_extra_adjust_half(ce,!dir); +} +void chan_extra_can_send( + struct chan_extra_map *chan_extra_map, + struct short_channel_id scid, + int dir, + struct amount_msat x) +{ + struct chan_extra *ce = chan_extra_map_get(chan_extra_map, + scid); + if(!ce) + { + debug_err("%s unexpected chan_extra ce is NULL", + __PRETTY_FUNCTION__); + } + if(!amount_msat_add(&x,x,ce->half[dir].htlc_total)) + { + debug_err("%s (line %d) cannot add x=%s and htlc_total=%s", + __PRETTY_FUNCTION__,__LINE__, + type_to_string(tmpctx,struct amount_msat,&x), + type_to_string(tmpctx,struct amount_msat,&ce->half[dir].htlc_total)); + } + chan_extra_can_send_(ce,dir,x); +} + +/* Update the knowledge that this (channel,direction) cannot send x msat.*/ +void chan_extra_cannot_send( + struct payment *p, + struct chan_extra_map *chan_extra_map, + struct short_channel_id scid, + int dir, + struct amount_msat x) +{ + struct chan_extra *ce = chan_extra_map_get(chan_extra_map, + scid); + if(!ce) + { + debug_err("%s (line %d) unexpected chan_extra ce is NULL", + __PRETTY_FUNCTION__,__LINE__); + } + + /* If a channel cannot send x it means that the upper bound for the + * liquidity is MAX_L < x + htlc_total */ + if(!amount_msat_add(&x,x,ce->half[dir].htlc_total)) + { + debug_err("%s (line %d) cannot add x=%s and htlc_total=%s", + __PRETTY_FUNCTION__,__LINE__, + type_to_string(tmpctx,struct amount_msat,&x), + type_to_string(tmpctx,struct amount_msat,&ce->half[dir].htlc_total)); + } + + if(!amount_msat_sub(&x,x,AMOUNT_MSAT(1))) + { + debug_err("%s (line %d) unexpected x=%s is less than 0msat", + __PRETTY_FUNCTION__,__LINE__, + type_to_string(tmpctx,struct amount_msat,&x) + ); + x = AMOUNT_MSAT(0); + } + + ce->half[dir].known_min = amount_msat_min(ce->half[dir].known_min,x); + ce->half[dir].known_max = amount_msat_min(ce->half[dir].known_max,x); + + debug_paynote(p,"Update chan knowledge scid=%s, dir=%d: [%s,%s]", + type_to_string(tmpctx,struct short_channel_id,&scid), + dir, + type_to_string(tmpctx,struct amount_msat,&ce->half[dir].known_min), + type_to_string(tmpctx,struct amount_msat,&ce->half[dir].known_max)); + + chan_extra_adjust_half(ce,!dir); +} +/* Update the knowledge that this (channel,direction) has liquidity x.*/ +static void chan_extra_set_liquidity_( + struct chan_extra *ce, + int dir, + struct amount_msat x) +{ + if(amount_msat_greater(x,ce->capacity)) + { + debug_err("%s unexpected capacity=%s is less than x=%s", + __PRETTY_FUNCTION__, + type_to_string(tmpctx,struct amount_msat,&ce->capacity), + type_to_string(tmpctx,struct amount_msat,&x) + ); + x = ce->capacity; + } + + ce->half[dir].known_min = x; + ce->half[dir].known_max = x; + + chan_extra_adjust_half(ce,!dir); +} +void chan_extra_set_liquidity( + struct chan_extra_map *chan_extra_map, + struct short_channel_id scid, + int dir, + struct amount_msat x) +{ + struct chan_extra *ce = chan_extra_map_get(chan_extra_map, + scid); + if(!ce) + { + debug_err("%s unexpected chan_extra ce is NULL", + __PRETTY_FUNCTION__); + } + chan_extra_set_liquidity_(ce,dir,x); +} +/* Update the knowledge that this (channel,direction) has sent x msat.*/ +static void chan_extra_sent_success_( + struct chan_extra *ce, + int dir, + struct amount_msat x) +{ + if(amount_msat_greater(x,ce->capacity)) + { + debug_err("%s unexpected capacity=%s is less than x=%s", + __PRETTY_FUNCTION__, + type_to_string(tmpctx,struct amount_msat,&ce->capacity), + type_to_string(tmpctx,struct amount_msat,&x) + ); + x = ce->capacity; + } + + struct amount_msat new_a, new_b; + + if(!amount_msat_sub(&new_a,ce->half[dir].known_min,x)) + new_a = AMOUNT_MSAT(0); + if(!amount_msat_sub(&new_b,ce->half[dir].known_max,x)) + new_b = AMOUNT_MSAT(0); + + ce->half[dir].known_min = new_a; + ce->half[dir].known_max = new_b; + + chan_extra_adjust_half(ce,!dir); +} +void chan_extra_sent_success( + struct chan_extra_map *chan_extra_map, + struct short_channel_id scid, + int dir, + struct amount_msat x) +{ + struct chan_extra *ce = chan_extra_map_get(chan_extra_map, + scid); + if(!ce) + { + debug_err("%s unexpected chan_extra ce is NULL", + __PRETTY_FUNCTION__); + } + chan_extra_sent_success_(ce,dir,x); +} +/* Forget a bit about this (channel,direction) state. */ +static void chan_extra_relax_( + struct chan_extra *ce, + int dir, + struct amount_msat down, + struct amount_msat up) +{ + struct amount_msat new_a, new_b; + + 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 = amount_msat_min(new_b,ce->capacity); + + ce->half[dir].known_min = new_a; + ce->half[dir].known_max = new_b; + + chan_extra_adjust_half(ce,!dir); +} +void chan_extra_relax( + struct chan_extra_map *chan_extra_map, + struct short_channel_id scid, + int dir, + struct amount_msat x, + struct amount_msat y) +{ + struct chan_extra *ce = chan_extra_map_get(chan_extra_map, + scid); + if(!ce) + { + debug_err("%s unexpected chan_extra ce is NULL", + __PRETTY_FUNCTION__); + } + chan_extra_relax_(ce,dir,x,y); +} + + +/* 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 scid, + int dir) +{ + struct chan_extra *ce; + + ce = chan_extra_map_get(chan_extra_map, scid); + if (!ce) + return NULL; + return &ce->half[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) +{ + return get_chan_extra_half_by_scid(chan_extra_map, + gossmap_chan_scid(gossmap, chan), + dir); +} + + +// 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) +{ + + const struct short_channel_id scid = gossmap_chan_scid(gossmap,chan); + struct chan_extra_half *h = get_chan_extra_half_by_scid( + chan_extra_map,scid,dir); + if (!h) { + struct amount_sat cap; + struct amount_msat cap_msat; + + if (!gossmap_chan_get_capacity(gossmap,chan, &cap) || + !amount_sat_to_msat(&cap_msat, cap)) + { + debug_err("%s (line %d) unable convert sat to msat or " + "get channel capacity", + __PRETTY_FUNCTION__, + __LINE__); + } + h = & new_chan_extra(chan_extra_map,scid,cap_msat)->half[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). + */ +static 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 tal_t *this_ctx = tal(tmpctx,tal_t); + + 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_add(&B,B,one)) + { + debug_err("%s (line %d) cannot add B=%s and %s", + __PRETTY_FUNCTION__, + __LINE__, + type_to_string(this_ctx, struct amount_msat, &B), + type_to_string(this_ctx, struct amount_msat, &one)); + } + // in_flight cannot be greater than max + if(!amount_msat_sub(&B,B,in_flight)) + { + debug_err("%s (line %d) in_flight=%s cannot be greater than B=%s", + __PRETTY_FUNCTION__, + __LINE__, + type_to_string(this_ctx, struct amount_msat, &in_flight), + type_to_string(this_ctx, struct amount_msat, &B)); + } + 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)) + { + debug_err("%s (line %d) B=%s must be greater than A=%s", + __PRETTY_FUNCTION__, + __LINE__, + type_to_string(this_ctx, struct amount_msat, &B), + type_to_string(this_ctx, struct amount_msat, &A)); + } + struct amount_msat numerator; // MAX(0,B-f) + + if(!amount_msat_sub(&numerator,B,f)) + numerator = AMOUNT_MSAT(0); + + tal_free(this_ctx); + return amount_msat_less_eq(f,A) ? 1.0 : amount_msat_ratio(numerator,denominator); +} + + + + + + +// TODO(eduardo): remove this function, is a duplicate +void remove_completed_flow(const struct gossmap *gossmap, + struct chan_extra_map *chan_extra_map, + struct flow *flow) +{ + for (size_t i = 0; i < tal_count(flow->path); i++) { + struct chan_extra_half *h = get_chan_extra_half_by_chan(gossmap, + chan_extra_map, + flow->path[i], + flow->dirs[i]); + if (!amount_msat_sub(&h->htlc_total, h->htlc_total, flow->amounts[i])) + { + debug_err("%s could not substract HTLC amounts, " + "half total htlc amount = %s, " + "flow->amounts[%lld] = %s.", + __PRETTY_FUNCTION__, + type_to_string(tmpctx, struct amount_msat, &h->htlc_total), + i, + type_to_string(tmpctx, struct amount_msat, &flow->amounts[i])); + } + if (h->num_htlcs == 0) + { + debug_err("%s could not decrease HTLC count.", + __PRETTY_FUNCTION__); + } + h->num_htlcs--; + } +} +// TODO(eduardo): remove this function, is a duplicate +void remove_completed_flow_set( + const struct gossmap *gossmap, + struct chan_extra_map *chan_extra_map, + struct flow **flows) +{ + for(size_t i=0;ipath); i++) { + struct chan_extra_half *h = get_chan_extra_half_by_chan(gossmap, + chan_extra_map, + flow->path[i], + flow->dirs[i]); + if (!amount_msat_add(&h->htlc_total, h->htlc_total, flow->amounts[i])) + { + debug_err("%s could not add HTLC amounts, " + "flow->amounts[%lld] = %s.", + __PRETTY_FUNCTION__, + i, + type_to_string(tmpctx, struct amount_msat, &flow->amounts[i])); + } + h->num_htlcs++; + } +} +// TODO(eduardo): remove this function, is a duplicate +void commit_flow_set( + const struct gossmap *gossmap, + struct chan_extra_map *chan_extra_map, + struct flow **flows) +{ + for(size_t i=0;isuccess_prob is misleading, because that's the prob. of + * success provided that there are no other flows in the current MPP flow set. + * */ +void flow_complete(struct flow *flow, + const struct gossmap *gossmap, + struct chan_extra_map *chan_extra_map, + struct amount_msat delivered) +{ + flow->success_prob = 1.0; + flow->amounts = tal_arr(flow, struct amount_msat, tal_count(flow->path)); + for (int i = tal_count(flow->path) - 1; i >= 0; i--) { + const struct chan_extra_half *h + = get_chan_extra_half_by_chan(gossmap, + chan_extra_map, + flow->path[i], + flow->dirs[i]); + + if(!h) + { + debug_err("%s unexpected chan_extra_half is NULL", + __PRETTY_FUNCTION__); + } + + flow->amounts[i] = delivered; + flow->success_prob + *= edge_probability(h->known_min, h->known_max, + h->htlc_total, + delivered); + + if (!amount_msat_add_fee(&delivered, + flow_edge(flow, i)->base_fee, + flow_edge(flow, i)->proportional_fee)) + { + debug_err("%s fee overflow", + __PRETTY_FUNCTION__); + } + } +} + +/* 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]; +}; + +// TODO(eduardo): here chan_extra_map should be const +// TODO(eduardo): here flows should be const +double flow_set_probability( + struct flow ** flows, + struct gossmap const*const gossmap, + struct chan_extra_map * chan_extra_map) +{ + tal_t *this_ctx = tal(tmpctx,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); + + for(size_t i=0;ipath);++j) + { + const struct chan_extra_half *h + = get_chan_extra_half_by_chan( + gossmap, + chan_extra_map, + f->path[j], + f->dirs[j]); + assert(h); + + const u32 c_idx = gossmap_chan_idx(gossmap,f->path[j]); + const int c_dir = f->dirs[j]; + + const struct amount_msat deliver = f->amounts[j]; + + struct amount_msat prev_flow; + if(!amount_msat_add(&prev_flow,h->htlc_total,in_flight[c_idx].half[c_dir])) + { + debug_err("%s (line %d) in-flight amount_msat overflow", + __PRETTY_FUNCTION__, + __LINE__); + } + + prob *= edge_probability(h->known_min,h->known_max, + prev_flow,deliver); + + if(!amount_msat_add(&in_flight[c_idx].half[c_dir], + in_flight[c_idx].half[c_dir], + deliver)) + { + debug_err("%s (line %d) in-flight amount_msat overflow", + __PRETTY_FUNCTION__, + __LINE__); + } + } + } + tal_free(this_ctx); + return prob; +} + +static int cmp_amount_msat(const struct amount_msat *a, + const struct amount_msat *b, + void *unused) +{ + if (amount_msat_less(*a, *b)) + return -1; + if (amount_msat_greater(*a, *b)) + return 1; + return 0; +} + +static int cmp_amount_sat(const struct amount_sat *a, + const struct amount_sat *b, + void *unused) +{ + if (amount_sat_less(*a, *b)) + return -1; + if (amount_sat_greater(*a, *b)) + return 1; + return 0; +} + +/* Get median feerates and capacities. */ +static void get_medians(const struct gossmap *gossmap, + struct amount_msat amount, + struct amount_msat *median_capacity, + struct amount_msat *median_fee) +{ + size_t num_caps, num_fees; + struct amount_sat *caps; + struct amount_msat *fees; + + caps = tal_arr(tmpctx, struct amount_sat, + gossmap_max_chan_idx(gossmap)); + fees = tal_arr(tmpctx, struct amount_msat, + gossmap_max_chan_idx(gossmap) * 2); + num_caps = num_fees = 0; + + for (struct gossmap_chan *c = gossmap_first_chan(gossmap); + c; + c = gossmap_next_chan(gossmap, c)) { + + /* If neither feerate is set, it's not useful */ + if (!gossmap_chan_set(c, 0) && !gossmap_chan_set(c, 1)) + continue; + + /* Insufficient capacity? Not useful */ + if (!gossmap_chan_get_capacity(gossmap, c, &caps[num_caps])) + continue; + if (amount_msat_greater_sat(amount, caps[num_caps])) + continue; + num_caps++; + + for (int dir = 0; dir <= 1; dir++) { + if (!gossmap_chan_set(c, dir)) + continue; + if (!amount_msat_fee(&fees[num_fees], + amount, + c->half[dir].base_fee, + c->half[dir].proportional_fee)) + continue; + num_fees++; + } + } + + asort(caps, num_caps, cmp_amount_sat, NULL); + /* If there are no channels, it doesn't really matter, but + * this avoids div by 0 */ + if (!num_caps) + *median_capacity = amount; + else if (!amount_sat_to_msat(median_capacity, caps[num_caps / 2])) + { + debug_err("%s (line %d) amount_msat overflow", + __PRETTY_FUNCTION__, + __LINE__); + } + asort(fees, num_fees, cmp_amount_msat, NULL); + if (!num_caps) + *median_fee = AMOUNT_MSAT(0); + else + *median_fee = fees[num_fees / 2]; +} + +double derive_mu(const struct gossmap *gossmap, + struct amount_msat amount, + double frugality) +{ + struct amount_msat median_capacity, median_fee; + double cap_plus_one; + + get_medians(gossmap, amount, &median_capacity, &median_fee); + + cap_plus_one = median_capacity.millisatoshis + 1; /* Raw: derive_mu */ + return -log((cap_plus_one - amount.millisatoshis) /* Raw: derive_mu */ + / cap_plus_one) + * frugality + /* +1 in case median fee is zero... */ + / (median_fee.millisatoshis + 1); /* Raw: derive_mu */ +} + +/* 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. + * */ +s64 linear_fee_cost( + const struct gossmap_chan *c, + const int dir, + double base_fee_penalty, + double delay_feefactor) +{ + 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; +} + +struct amount_msat flow_set_fee(struct flow **flows) +{ + struct amount_msat fee = AMOUNT_MSAT(0); + + for (size_t i = 0; i < tal_count(flows); i++) { + struct amount_msat this_fee; + size_t n = tal_count(flows[i]->amounts); + + if (!amount_msat_sub(&this_fee, + flows[i]->amounts[0], + flows[i]->amounts[n-1])) + { + debug_err("%s (line %d) amount_msat overflow", + __PRETTY_FUNCTION__, + __LINE__); + } + if(!amount_msat_add(&fee, this_fee,fee)) + { + debug_err("%s (line %d) amount_msat overflow", + __PRETTY_FUNCTION__, + __LINE__); + } + } + return fee; +} + +/* Helper to access the half chan at flow index idx */ +const struct half_chan *flow_edge(const struct flow *flow, size_t idx) +{ + assert(idx < tal_count(flow->path)); + return &flow->path[idx]->half[flow->dirs[idx]]; +} + +#ifndef SUPERVERBOSE_ENABLED +#undef SUPERVERBOSE +#endif diff --git a/plugins/renepay/flow.h b/plugins/renepay/flow.h new file mode 100644 index 000000000..c1043486b --- /dev/null +++ b/plugins/renepay/flow.h @@ -0,0 +1,319 @@ +#ifndef LIGHTNING_PLUGINS_RENEPAY_FLOW_H +#define LIGHTNING_PLUGINS_RENEPAY_FLOW_H +#include "config.h" +#include +#include +#include +#include +#include + +// TODO(eduardo): 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_CAP (AMOUNT_MSAT(21000000*MSAT_PER_BTC)) + +/* 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(struct chan_extra const * const ce); + +static inline const struct short_channel_id +chan_extra_scid(const struct chan_extra *cd) +{ + return cd->scid; +} + +static inline size_t hash_scid(const struct short_channel_id scid) +{ + /* scids cost money to generate, so simple hash works here */ + return (scid.u64 >> 32) + ^ (scid.u64 >> 16) + ^ scid.u64; +} + +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_TYPE(struct chan_extra, + chan_extra_scid, hash_scid, 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); + +/* 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); + + +/* This helper function preserves the uncertainty network invariant after the + * knowledge is updated. It assumes that the (channel,!dir) knowledge is + * correct. */ +void chan_extra_adjust_half(struct chan_extra *ce, + int dir); + +/* Helper to find the min of two amounts */ +static inline struct amount_msat amount_msat_min( + struct amount_msat a, + struct amount_msat b) +{ + return amount_msat_less(a,b) ? a : b; +} +/* Helper to find the max of two amounts */ +static inline struct amount_msat amount_msat_max( + struct amount_msat a, + struct amount_msat b) +{ + return amount_msat_greater(a,b) ? a : b; +} + +/* Update the knowledge that this (channel,direction) can send x msat.*/ +void chan_extra_can_send(struct chan_extra_map *chan_extra_map, + struct short_channel_id scid, + int dir, + struct amount_msat x); + +/* Update the knowledge that this (channel,direction) cannot send x msat.*/ +void chan_extra_cannot_send(struct payment* p, + struct chan_extra_map *chan_extra_map, + struct short_channel_id scid, + int dir, + struct amount_msat x); + +/* Update the knowledge that this (channel,direction) has liquidity x.*/ +void chan_extra_set_liquidity(struct chan_extra_map *chan_extra_map, + struct short_channel_id scid, + int dir, + struct amount_msat x); + +/* Update the knowledge that this (channel,direction) has sent x msat.*/ +void chan_extra_sent_success(struct chan_extra_map *chan_extra_map, + struct short_channel_id scid, + int dir, + struct amount_msat x); + +/* Forget a bit about this (channel,direction) state. */ +void chan_extra_relax(struct chan_extra_map *chan_extra_map, + struct short_channel_id scid, + int dir, + struct amount_msat down, + struct amount_msat up); + + +/* 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 scid, + int dir); +/* 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); + +/* An actual partial flow. */ +struct flow { + struct gossmap_chan const **path; + /* The directions to traverse. */ + int *dirs; + /* Amounts for this flow (fees mean this shrinks across path). */ + struct amount_msat *amounts; + /* Probability of success (0-1) */ + double success_prob; +}; + +/* 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); + +/* Function to fill in amounts and success_prob for flow. */ +void flow_complete(struct flow *flow, + const struct gossmap *gossmap, + struct chan_extra_map *chan_extra_map, + struct amount_msat delivered); + +/* Compute the prob. of success of a set of concurrent set of flows. */ +double flow_set_probability( + struct flow ** flows, + struct gossmap const*const gossmap, + struct chan_extra_map * chan_extra_map); + +// TODO(eduardo): we probably don't need this. Instead we should have payflow +// input. +/* Once flow is completed, this can remove it from the extra_map */ +void remove_completed_flow(const struct gossmap *gossmap, + struct chan_extra_map *chan_extra_map, + struct flow *flow); +// TODO(eduardo): we probably don't need this. Instead we should have payflow +// input. +void remove_completed_flow_set(const struct gossmap *gossmap, + struct chan_extra_map *chan_extra_map, + struct flow **flows); + +struct amount_msat flow_set_fee(struct flow **flows); + +/* + * mu (μ) is used as follows in the cost function: + * + * -log((c_e + 1 - f_e) / (c_e + 1)) + μ fee + * + * This raises the question of how to set mu? The left term is a + * logrithmic failure probability, the right term is the fee in + * millisats. + * + * We want a more "usable" measure of frugality (fr), where fr = 1 + * means that the two terms are roughly equal, and fr < 1 means the + * probability is more important, and fr > 1 means the fee is more + * important. + * + * For this we take the current payment amount and the median channel + * capacity and feerates: + * + * -log((median_cap + 1 - f_e) / (median_cap + 1)) = μ (1/fr) median_fee + * + * => μ = -log((median_cap + 1 - f_e) / (median_cap + 1)) * fr / median_fee + * + * But this is slightly too naive! If we're trying to make a payment larger + * than the median, this is undefined; and grows hugely when we're near the median. + * In fact, it should be "the median of all channels larger than the amount", + * which is what we calculate here. + * + * Turns out that in the real network: + * - median_cap = 1250800000 + * - median_feerate = 51 + * + * And the log term at the 10th percentile capacity is about 0.125 of the median, + * and at the 90th percentile capacity the log term is about 12.5 the value at the median. + * + * In other words, we expose a simple frugality knob with reasonable + * range 0.1 (don't care about fees) to 10 (fees before probability), + * and generate our μ from there. + */ +double derive_mu(const struct gossmap *gossmap, + struct amount_msat amount, + double frugality); + +s64 linear_fee_cost( + const struct gossmap_chan *c, + const int dir, + double base_fee_penalty, + double delay_feefactor); + +// TODO(eduardo): we probably don't need this. Instead we should have payflow +// input. +/* Take the flows and commit them to the chan_extra's . */ +void commit_flow( + const struct gossmap *gossmap, + struct chan_extra_map *chan_extra_map, + struct flow *flow); + +// TODO(eduardo): we probably don't need this. Instead we should have payflow +// input. +/* Take the flows and commit them to the chan_extra's . */ +void commit_flow_set( + const struct gossmap *gossmap, + struct chan_extra_map *chan_extra_map, + struct flow **flows); + +#endif /* LIGHTNING_PLUGINS_RENEPAY_FLOW_H */ diff --git a/plugins/renepay/heap.h b/plugins/renepay/heap.h new file mode 100644 index 000000000..b06cd8f04 --- /dev/null +++ b/plugins/renepay/heap.h @@ -0,0 +1,195 @@ +#ifndef LIGHTNING_PLUGINS_RENEPAY_HEAP_H +#define LIGHTNING_PLUGINS_RENEPAY_HEAP_H +#include "config.h" +#include +#include +#include + + +/* A functionality missing in gheap that can be used to update elements. + * Input: item + * Output: the position of the smallest element p, such is greater equal item. + * Formally: + * Let X={x in heap: !(xdistance, + db = ((struct heap_data*)b)->distance; + u32 ia = ((struct heap_data*)a)->idx, + ib = ((struct heap_data*)b)->idx; + return da==db ? ia > ib : da > db; +} + +static void item_mover(void *const dst, const void *const src) +{ + *(struct heap_data*)dst = *(struct heap_data*)src; +} + +struct heap* heap_new(const tal_t *ctx, const size_t max_capacity) +{ + struct heap* heap = tal(ctx,struct heap); + heap->size=0; + heap->data = tal_arr(heap,struct heap_data,max_capacity); + heap->max_size = max_capacity; + + heap->gheap_ctx.fanout=2; + heap->gheap_ctx.page_chunks=1; + heap->gheap_ctx.item_size= sizeof(struct heap_data); + heap->gheap_ctx.less_comparer=less_comparer; + heap->gheap_ctx.less_comparer_ctx=heap; + heap->gheap_ctx.item_mover=item_mover; + + return heap; +} + + +void heap_insert(struct heap* heap, u32 idx, s64 distance) +{ + heap->data[heap->size].idx=idx; + heap->data[heap->size].distance=distance; + heap->size++; + + assert(heap->size<=heap->max_size); + + gheap_restore_heap_after_item_increase(&heap->gheap_ctx, + heap->data, + heap->size, + heap->size-1); +} +bool heap_empty(const struct heap* heap) +{ + return heap->size==0; +} +struct heap_data * heap_top(const struct heap * heap) +{ + return &heap->data[0]; +} +void heap_pop(struct heap* heap) +{ + if(heap->size>0) + gheap_pop_heap(&heap->gheap_ctx,heap->data,heap->size--); +} + +/* Input: item + * Output: the smallest x such that !(xfanout; + const size_t item_size = ctx->item_size; + const void*const less_comparer_ctx = ctx->less_comparer_ctx; + const gheap_less_comparer_t less_comparer = ctx->less_comparer; + + if(less_comparer(less_comparer_ctx,base,item)) + { + // root=heap_size) + break; + if(!less_comparer(less_comparer_ctx, + ((char*)base) + child*item_size, + item)) + { + // satisfies the condition, + // is it the smallest one? + if(!less_comparer(less_comparer_ctx, + ((char*)base) + best_child*item_size, + ((char*)base) + child*item_size)) + { + // child <= best_child, so child is a + // better upper bound + best_child = child; + } + } + } + + if(best_child==last) + { + // no change, we stop + break; + } + last = best_child; + } + return last; +} +void heap_update(struct heap* heap, u32 idx, s64 old_distance, s64 new_distance) +{ + const gheap_less_comparer_t less_comparer = heap->gheap_ctx.less_comparer; + const void *const less_comparer_ctx = heap->gheap_ctx.less_comparer_ctx; + + struct heap_data old_item = (struct heap_data){.idx=idx, .distance=old_distance}; + + size_t pos = gheap_upper_bound(&heap->gheap_ctx,heap->data,heap->size,&old_item); + if(pos>=heap->size || heap->data[pos].idx!=idx) + { + heap_insert(heap,idx,new_distance); + } + else + { + struct heap_data new_item = (struct heap_data){.idx=idx, .distance=new_distance}; + + if(less_comparer(less_comparer_ctx,&new_item,&heap->data[pos])) + { + heap->data[pos].distance = new_distance; + gheap_restore_heap_after_item_decrease( + &heap->gheap_ctx, + heap->data, + heap->size, + pos); + }else + { + heap->data[pos].distance = new_distance; + gheap_restore_heap_after_item_increase( + &heap->gheap_ctx, + heap->data, + heap->size, + pos); + } + } +} + +#endif /* LIGHTNING_PLUGINS_RENEPAY_HEAP_H */ diff --git a/plugins/renepay/mcf.c b/plugins/renepay/mcf.c new file mode 100644 index 000000000..4629d024b --- /dev/null +++ b/plugins/renepay/mcf.c @@ -0,0 +1,1507 @@ +#include "config.h" +#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}; + +// how many bits for linearization parts plus 1 bit for the direction of the +// channel plus 1 bit for the dual representation. +static const size_t ARC_ADDITIONAL_BITS = PARTS_BITS + 2; + +static const s64 INFINITE = INT64_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. + * + * */ +typedef union +{ + struct{ + u32 dual: 1; + u32 part: PARTS_BITS; + u32 chandir: 1; + u32 chanidx: (32-1-PARTS_BITS-1); + }; + u32 idx; +} arc_t; + +#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; + struct gossmap_node const*source; + struct gossmap_node const*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 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) + + arc_t *node_adjacency_next_arc; + arc_t *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 arc_t arc_dual(arc_t arc) +{ + arc.dual ^= 1; + return arc; +} +/* Helper function. */ +static bool arc_is_dual(const arc_t arc) +{ + return arc.dual == 1; +} + +/* 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 arc_t 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 arc_t 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 arc_t arc) +{ + const arc_t 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 arc_t 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 arc_t 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 arc_t node_adjacency_next( + const struct linear_network *linear_network, + const arc_t arc) +{ + assert(arc.idx < tal_count(linear_network->node_adjacency_next_arc)); + return linear_network->node_adjacency_next_arc[arc.idx]; +} + +/* Helper function. + * Given a channel index, we should be able to deduce the arc id. */ +static arc_t channel_idx_to_arc( + const u32 chan_idx, + int half, + int part, + int dual) +{ + arc_t arc; + // arc.idx=0; // shouldn't be necessary, but valgrind complains of uninitialized field idx + arc.dual=dual; + arc.part=part; + arc.chandir=half; + arc.chanidx = chan_idx; + return arc; +} + +// TODO(eduardo): unit test this +/* Split a directed channel into parts with linear cost function. */ +static void 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) + { + debug_err("%s (line %d) unexpected, extra_half is NULL", + __PRETTY_FUNCTION__, + __LINE__); + } + + s64 a = extra_half->known_min.millisatoshis/1000, /* Raw: linearize_channel */ + b = 1 + extra_half->known_max.millisatoshis/1000; /* Raw: linearize_channel */ + + capacity[0]=a; + cost[0]=0; + for(size_t i=1;icap_fraction[i]*(b-a); + + cost[i] = params->cost_fraction[i] + *params->amount.millisatoshis /* Raw: linearize_channel */ + *params->prob_cost_factor*1.0/(b-a); + } +} + +static void alloc_residual_netork( + 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; + + 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); +} +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(u32 idx=0;idxcap[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(u32 arc_idx=0;arc_idxmax_num_arcs;++arc_idx) + { + arc_t arc = (arc_t){.idx=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 arc_t 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 arc_t 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; +} + + +static void init_linear_network( + const struct pay_parameters *params, + struct linear_network *linear_network) +{ + const size_t max_num_chans = gossmap_max_chan_idx(params->gossmap); + const size_t max_num_arcs = max_num_chans << ARC_ADDITIONAL_BITS; + 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); + 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,arc_t,max_num_arcs); + 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,arc_t,max_num_nodes); + 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); + 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); + 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); + + 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); + + // TODO(eduardo): in which case can this be triggered? + if (!gossmap_chan_set(c,half)) + continue; + + const u32 chan_id = gossmap_chan_idx(params->gossmap, c); + + if (params->disabled && bitmap_test_bit(params->disabled,chan_id)) + continue; + + + 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` + linearize_channel(params,c,half,capacity,prob_cost); + + 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 + arc_t 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; + } + } + } +} + +/* 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. + * Returns RENEPAY_ERR_OK if the path exists. */ +static int find_admissible_path( + const struct linear_network *linear_network, + const struct residual_network *residual_network, + const u32 source, + const u32 target, + arc_t *prev) +{ + tal_t *this_ctx = tal(tmpctx,tal_t); + + int ret = RENEPAY_ERR_NOFEASIBLEFLOW; + 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) + { + ret = RENEPAY_ERR_OK; + break; + } + + for(arc_t 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(tmpctx,struct queue_data); + qdata->idx = next; + lqueue_enqueue(&myqueue,qdata); + } + } + tal_free(this_ctx); + return ret; +} + +/* 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 arc_t *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 arc_t *prev, + s64 flow) +{ + u32 cur = target; + + while(cur!=source) + { + assert(cur < tal_count(prev)); + const arc_t arc = prev[cur]; + const arc_t 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 int find_feasible_flow( + const struct linear_network *linear_network, + struct residual_network *residual_network, + const u32 source, + const u32 target, + s64 amount) +{ + assert(amount>=0); + + tal_t *this_ctx = tal(tmpctx,tal_t); + int ret = RENEPAY_ERR_OK; + + /* path information + * prev: is the id of the arc that lead to the node. */ + arc_t *prev = tal_arr(this_ctx,arc_t,linear_network->max_num_nodes); + + while(amount>0) + { + // find a path from source to target + int err = find_admissible_path( + linear_network, + residual_network,source,target,prev); + + if(err!=RENEPAY_ERR_OK) + { + ret = RENEPAY_ERR_NOFEASIBLEFLOW; + break; + } + + // 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); + augment_flow(linear_network,residual_network,source,target,prev,delta); + + assert(delta>0 && delta<=amount); + amount -= delta; + } + + tal_free(this_ctx); + return ret; +} + +// 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 int find_optimal_path( + const struct linear_network *linear_network, + const struct residual_network* residual_network, + const u32 source, + const u32 target, + arc_t *prev) +{ + tal_t *this_ctx = tal(tmpctx,tal_t); + int ret = RENEPAY_ERR_NOFEASIBLEFLOW; + + bitmap *visited = tal_arrz(this_ctx, bitmap, + BITMAP_NWORDS(linear_network->max_num_nodes)); + + 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(next,distance[cur]+cij); + prev[next]=arc; + } + } + tal_free(this_ctx); + return ret; +} + +/* 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(arc_t arc=node_adjacency_begin(linear_network,node); + !node_adjacency_end(arc); + arc = node_adjacency_next(linear_network,arc)) + { + if(arc_is_dual(arc))continue; + + arc_t 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 int optimize_mcf( + const struct linear_network *linear_network, + struct residual_network *residual_network, + const u32 source, + const u32 target, + const s64 amount) +{ + assert(amount>=0); + tal_t *this_ctx = tal(tmpctx,tal_t); + + int ret = RENEPAY_ERR_OK; + + zero_flow(linear_network,residual_network); + arc_t *prev = tal_arr(this_ctx,arc_t,linear_network->max_num_nodes); + + s64 const*const distance = dijkstra_distance_data(); + + s64 remaining_amount = amount; + + while(remaining_amount>0) + { + int err = find_optimal_path(linear_network,residual_network,source,target,prev); + if(err!=RENEPAY_ERR_OK) + { + // unexpected error + ret = RENEPAY_ERR_NOFEASIBLEFLOW; + break; + } + + // 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); + augment_flow(linear_network,residual_network,source,target,prev,delta); + + assert(delta>0 && delta<=remaining_amount); + 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 ret; +} + +// 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 struct chan_flow *chan_flow, + const u32 start_idx, + const s64 *balance, + + struct gossmap_chan const** 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",__PRETTY_FUNCTION__,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; + struct gossmap_chan const *c + = gossmap_nth_chan(gossmap, + cur,i,&dir); + + if (!gossmap_chan_set(c,dir)) + 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; +}; + +// TODO(eduardo): check this +/* 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, + + // 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) +{ + assert(amount_msat_less(excess, AMOUNT_MSAT(1000))); + + tal_t *this_ctx = tal(tmpctx,tal_t); + + 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); + + struct gossmap_chan const **prev_chan + = tal_arr(this_ctx,struct gossmap_chan const*,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); + + // Convert the arc based residual network flow into a flow in the + // directed channel network. + // Compute balance on the nodes. + for(u32 n = 0;npath = tal_arr(fp,struct gossmap_chan const*,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]; + struct gossmap_chan const * 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)) + { + debug_err("%s (line %d) unable to substract excess.", + __PRETTY_FUNCTION__, + __LINE__); + } + excess = amount_msat(0); + + // complete the flow path by adding real fees and + // probabilities. + flow_complete(fp,gossmap,chan_extra_map,delivered); + + // add fp to list + ld = tal(list_ctx,struct list_data); + ld->flow_path = fp; + list_add(&path_list,&ld->list); + num_paths++; + } + } + + // copy the list into the array we are going to return + struct flow **flows = tal_arr(ctx,struct flow*,num_paths); + size_t pos=0; + list_for_each(&path_list,ld,list) + { + flows[pos++] = tal_steal(flows,ld->flow_path); + } + + tal_free(this_ctx); + return flows; +} + +/* Given the constraints on max fee and min prob., + * is the flow A better than B? */ +static bool is_better( + struct amount_msat max_fee, + double min_probability, + + struct amount_msat A_fee, + double A_prob, + + struct amount_msat B_fee, + double B_prob) +{ + bool A_fee_pass = amount_msat_less_eq(A_fee,max_fee); + bool B_fee_pass = amount_msat_less_eq(B_fee,max_fee); + bool A_prob_pass = A_prob >= 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); +} + + +// 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 delay_feefactor, + double base_fee_penalty, + u32 prob_cost_factor ) +{ + tal_t *this_ctx = tal(tmpctx,tal_t); + + struct pay_parameters *params = tal(this_ctx,struct pay_parameters); + + params->gossmap = gossmap; + params->source = source; + params->target = target; + params->chan_extra_map = chan_extra_map; + + params->disabled = disabled; + assert(!disabled + || tal_bytelen(disabled) == bitmap_sizeof(gossmap_max_chan_idx(gossmap))); + + 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; + + // build the uncertainty network with linearization and residual arcs + struct linear_network *linear_network= tal(this_ctx,struct linear_network); + init_linear_network(params,linear_network); + + struct residual_network *residual_network = tal(this_ctx,struct residual_network); + alloc_residual_netork(linear_network,residual_network); + + dijkstra_malloc(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; + struct flow **best_flow_paths = NULL; + + /* 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); + + int err = find_feasible_flow(linear_network,residual_network,source_idx,target_idx, + pay_amount_sats); + + if(err!=RENEPAY_ERR_OK) + { + // there is no flow that satisfy the constraints, we stop here + goto finish; + } + + // first flow found + best_flow_paths = get_flow_paths(ctx,params->gossmap,params->chan_extra_map, + linear_network,residual_network, + excess); + best_prob_success = flow_set_probability(best_flow_paths, + params->gossmap, + params->chan_extra_map); + best_fee = flow_set_fee(best_flow_paths); + + // 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->chan_extra_map, + linear_network,residual_network, + excess); + + double prob_success = flow_set_probability( + flow_paths, + params->gossmap, + params->chan_extra_map); + struct amount_msat fee = flow_set_fee(flow_paths); + + // 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_steal(ctx,flow_paths); + best_fee = fee; + best_prob_success=prob_success; + flow_paths = NULL; + } + + 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; + } + + if(flow_paths) + tal_free(flow_paths); + } + + + + finish: + + tal_free(this_ctx); + return best_flow_paths; +} + diff --git a/plugins/renepay/mcf.h b/plugins/renepay/mcf.h new file mode 100644 index 000000000..151e9bec8 --- /dev/null +++ b/plugins/renepay/mcf.h @@ -0,0 +1,71 @@ +#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 delay_feefactor, + double base_fee_penalty, + u32 prob_cost_factor); +#endif /* LIGHTNING_PLUGINS_RENEPAY_MCF_H */ diff --git a/plugins/renepay/pay.c b/plugins/renepay/pay.c new file mode 100644 index 000000000..722c3803d --- /dev/null +++ b/plugins/renepay/pay.c @@ -0,0 +1,1792 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// TODO(eduardo): maybe there are too many debug_err and plugin_err and +// plugin_log(...,LOG_BROKEN,...) that could be resolved with a command_fail + +#define INVALID_ID UINT64_MAX +#define MAX(a,b) ((a)>(b)? (a) : (b)) + +static struct pay_plugin the_pay_plugin; +struct pay_plugin * const pay_plugin = &the_pay_plugin; + +static void timer_kick(struct renepay * renepay); +static struct command_result *try_paying(struct command *cmd, + struct renepay * renepay, + bool first_time); + +// TODO(eduardo): maybe we don't need these +static void background_timer_kick(void*p UNUSED); +static void background_settimer(void); + +void amount_msat_accumulate_(struct amount_msat *dst, + struct amount_msat src, + const char *dstname, + const char *srcname) +{ + if (amount_msat_add(dst, *dst, src)) + return; + debug_err("Overflow adding %s (%s) into %s (%s)", + srcname, type_to_string(tmpctx, struct amount_msat, &src), + dstname, type_to_string(tmpctx, struct amount_msat, dst)); +} + +void amount_msat_reduce_(struct amount_msat *dst, + struct amount_msat src, + const char *dstname, + const char *srcname) +{ + if (amount_msat_sub(dst, *dst, src)) + return; + debug_err("Underflow subtracting %s (%s) from %s (%s)", + srcname, type_to_string(tmpctx, struct amount_msat, &src), + dstname, type_to_string(tmpctx, struct amount_msat, dst)); +} + + +#if DEVELOPER +static void memleak_mark(struct plugin *p, struct htable *memtable) +{ + /* TODO(eduardo): understand the purpose of memleak_scan_obj, why use it + * instead of tal_free? + * 1st problem: this is executed before the plugin can process the + * shutdown notification, + * 2nd problem: memleak_scan_obj does not propagate to children. + * For the moment let's just (incorrectly) do tal_free here + * */ + pay_plugin->ctx = tal_free(pay_plugin->ctx); + + // memleak_scan_obj(memtable, pay_plugin->ctx); + // memleak_scan_obj(memtable, pay_plugin->gossmap); + // memleak_scan_obj(memtable, pay_plugin->chan_extra_map); + // memleak_scan_htable(memtable, &pay_plugin->chan_extra_map->raw); +} +#endif + +static void destroy_payflow(struct pay_flow *flow) +{ + remove_htlc_payflow(pay_plugin->chan_extra_map,flow); + payflow_map_del(pay_plugin->payflow_map, flow); +} + +static const char *init(struct plugin *p, + const char *buf UNUSED, const jsmntok_t *config UNUSED) +{ + size_t num_channel_updates_rejected; + + pay_plugin->ctx = notleak_with_children(tal(p,tal_t)); + pay_plugin->plugin = p; + pay_plugin->rexmit_timer=NULL; + + rpc_scan(p, "getinfo", take(json_out_obj(NULL, NULL, NULL)), + "{id:%}", JSON_SCAN(json_to_node_id, &pay_plugin->my_id)); + + rpc_scan(p, "listconfigs", + take(json_out_obj(NULL, NULL, NULL)), + "{configs:" + "{max-locktime-blocks:{value_int:%}," + "experimental-offers:{set:%}}}", + JSON_SCAN(json_to_number, &pay_plugin->maxdelay_default), + JSON_SCAN(json_to_bool, &pay_plugin->exp_offers) + ); + + list_head_init(&pay_plugin->payments); + + pay_plugin->chan_extra_map = tal(pay_plugin->ctx,struct chan_extra_map); + chan_extra_map_init(pay_plugin->chan_extra_map); + + pay_plugin->payflow_map = tal(pay_plugin->ctx,struct payflow_map); + payflow_map_init(pay_plugin->payflow_map); + + pay_plugin->gossmap = gossmap_load(pay_plugin->ctx, + GOSSIP_STORE_FILENAME, + &num_channel_updates_rejected); + + if (!pay_plugin->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); + + uncertainty_network_update(pay_plugin->gossmap, + pay_plugin->chan_extra_map); +#if DEVELOPER + plugin_set_memleak_handler(p, memleak_mark); +#endif + + background_settimer(); + return NULL; +} + + +// /* TODO(eduardo): an example of an RPC call that is not bound to any command. */ +//static +//struct command_result* getinfo_done(struct command *cmd UNUSED, +// const char *buf, +// const jsmntok_t *result, +// void* pp UNUSED) +//{ +// struct node_id id; +// const jsmntok_t *id_tok = json_get_member(buf,result,"id"); +// json_to_node_id(buf,id_tok,&id); +// +// plugin_log(pay_plugin->plugin,LOG_DBG, +// "calling %s, nodeid = %s", +// __PRETTY_FUNCTION__, +// type_to_string(tmpctx,struct node_id,&id)); +// +// return command_still_pending(NULL); +//} + +static void background_settimer(void) +{ + pay_plugin->rexmit_timer + = tal_free(pay_plugin->rexmit_timer); + pay_plugin->rexmit_timer + = plugin_timer( + pay_plugin->plugin, + time_from_msec(2000), + background_timer_kick, NULL); +} + +static void background_timer_kick(void * p UNUSED) +{ + // plugin_log(pay_plugin->plugin,LOG_DBG,"calling %s",__PRETTY_FUNCTION__); + background_settimer(); + + // /* TODO(eduardo): an example of an RPC call that is not bound to any command. */ + // struct out_req * req = jsonrpc_request_start(pay_plugin->plugin, + // NULL, + // "getinfo", + // getinfo_done, + // getinfo_done, + // NULL); + // send_outreq(pay_plugin->plugin, req); +} + +static void renepay_settimer(struct renepay * renepay) +{ + renepay->rexmit_timer = tal_free(renepay->rexmit_timer); + renepay->rexmit_timer = plugin_timer( + pay_plugin->plugin, + time_from_msec(TIMER_COLLECT_FAILURES_MSEC), + timer_kick, renepay); +} + +/* Happens when timer goes off, but also works to arm timer if nothing to do */ +static void timer_kick(struct renepay * renepay) +{ + struct payment * const p = renepay->payment; + plugin_log(pay_plugin->plugin,LOG_DBG,"calling %s",__PRETTY_FUNCTION__); + + switch(p->status) + { + /* Some flows succeeded, we finish the payment. */ + case PAYMENT_SUCCESS: + renepay_success(renepay); + break; + + /* Some flows failed, we retry. */ + case PAYMENT_FAIL: + payment_assert_delivering_incomplete(p); + try_paying(renepay->cmd,renepay,false); + break; + + /* Nothing has returned yet, we have to wait. */ + case PAYMENT_PENDING: + payment_assert_delivering_all(p); + renepay_settimer(renepay); + break; + } +} + +/* Sometimes we don't know exactly who to blame... */ +static struct command_result *handle_unhandleable_error(struct renepay * renepay, + struct pay_flow *flow, + const char *what) +{ + struct payment * const p = renepay->payment; + plugin_log(pay_plugin->plugin,LOG_DBG,"calling %s",__PRETTY_FUNCTION__); + size_t n = tal_count(flow); + + /* We got a mangled reply. We don't know who to penalize! */ + debug_paynote(p, "%s on route %s", what, flow_path_to_str(tmpctx, flow)); + + // TODO(eduardo): does LOG_BROKEN finish the plugin execution? + plugin_log(pay_plugin->plugin, LOG_BROKEN, + "%s on route %s", + what, flow_path_to_str(tmpctx, flow)); + + if (n == 1) + { + payflow_fail(flow); + return renepay_fail(renepay, PAY_UNPARSEABLE_ONION, + "Got %s from the destination", what); + } + /* FIXME: check chan_extra_map, since we might have succeeded though + * this node before? */ + + /* Prefer a node not directly connected to either end. */ + if (n > 3) { + /* us ->0-> ourpeer ->1-> rando ->2-> theirpeer ->3-> dest */ + n = 1 + pseudorand(n - 2); + } else + /* Assume it's not the destination */ + n = pseudorand(n-1); + + tal_arr_expand(&renepay->disabled, flow->path_scids[n]); + debug_paynote(p, "... eliminated %s", + type_to_string(tmpctx, struct short_channel_id, + &flow->path_scids[n])); + return NULL; +} + +/* We hold onto the flow (and delete the timer) while we're waiting for + * gossipd to receive the channel_update we got from the error. */ +struct addgossip { + struct short_channel_id scid; + struct pay_flow *flow; +}; + +static struct command_result *addgossip_done(struct command *cmd, + const char *buf, + const jsmntok_t *err, + struct addgossip *adg) +{ + plugin_log(pay_plugin->plugin,LOG_DBG,"calling %s",__PRETTY_FUNCTION__); + struct renepay * renepay = adg->flow->payment->renepay; + + /* Release this: if it's the last flow we'll retry immediately */ + + payflow_fail(adg->flow); + tal_free(adg); + renepay_settimer(renepay); + + return command_still_pending(cmd); +} + +static struct command_result *addgossip_failure(struct command *cmd, + const char *buf, + const jsmntok_t *err, + struct addgossip *adg) + +{ + plugin_log(pay_plugin->plugin,LOG_DBG,"calling %s",__PRETTY_FUNCTION__); + struct payment * p = adg->flow->payment; + struct renepay * renepay = p->renepay; + + debug_paynote(p, "addgossip failed, removing channel %s (%.*s)", + type_to_string(tmpctx, struct short_channel_id, &adg->scid), + err->end - err->start, buf + err->start); + tal_arr_expand(&renepay->disabled, adg->scid); + + return addgossip_done(cmd, buf, err, adg); +} + +static struct command_result *submit_update(struct command *cmd, + struct pay_flow *flow, + const u8 *update, + struct short_channel_id errscid) +{ + plugin_log(pay_plugin->plugin,LOG_DBG,"calling %s",__PRETTY_FUNCTION__); + struct payment * p = flow->payment; + struct renepay * renepay = p->renepay; + struct out_req *req; + struct addgossip *adg = tal(cmd, struct addgossip); + + /* We need to stash scid in case this fails, and we need to hold flow so + * we don't get a rexmit before this is complete. */ + adg->scid = errscid; + adg->flow = flow; + /* Disable re-xmit until this returns */ + renepay->rexmit_timer + = tal_free(renepay->rexmit_timer); + + debug_paynote(p, "... extracted channel_update, telling gossipd"); + plugin_log(pay_plugin->plugin, LOG_DBG, "(update = %s)", tal_hex(tmpctx, update)); + + req = jsonrpc_request_start(pay_plugin->plugin, NULL, "addgossip", + addgossip_done, + addgossip_failure, + adg); + json_add_hex_talarr(req->js, "message", update); + return send_outreq(pay_plugin->plugin, req); +} + +/* Fix up the channel_update to include the type if it doesn't currently have + * one. See ElementsProject/lightning#1730 and lightningnetwork/lnd#1599 for the + * in-depth discussion on why we break message parsing here... */ +static u8 *patch_channel_update(const tal_t *ctx, u8 *channel_update TAKES) +{ + u8 *fixed; + if (channel_update != NULL && + fromwire_peektype(channel_update) != WIRE_CHANNEL_UPDATE) { + /* This should be a channel_update, prefix with the + * WIRE_CHANNEL_UPDATE type, but isn't. Let's prefix it. */ + fixed = tal_arr(ctx, u8, 0); + towire_u16(&fixed, WIRE_CHANNEL_UPDATE); + towire(&fixed, channel_update, tal_bytelen(channel_update)); + if (taken(channel_update)) + tal_free(channel_update); + return fixed; + } else { + return tal_dup_talarr(ctx, u8, channel_update); + } +} + + +/* Return NULL if the wrapped onion error message has no channel_update field, + * or return the embedded channel_update message otherwise. */ +static u8 *channel_update_from_onion_error(const tal_t *ctx, + const char *buf, + const jsmntok_t *onionmsgtok) +{ + u8 *channel_update = NULL; + struct amount_msat unused_msat; + u32 unused32; + u8 *onion_message = json_tok_bin_from_hex(tmpctx, buf, onionmsgtok); + + /* Identify failcodes that have some channel_update. + * + * TODO > BOLT 1.0: Add new failcodes when updating to a + * new BOLT version. */ + if (!fromwire_temporary_channel_failure(ctx, + onion_message, + &channel_update) && + !fromwire_amount_below_minimum(ctx, + onion_message, &unused_msat, + &channel_update) && + !fromwire_fee_insufficient(ctx, + onion_message, &unused_msat, + &channel_update) && + !fromwire_incorrect_cltv_expiry(ctx, + onion_message, &unused32, + &channel_update) && + !fromwire_expiry_too_soon(ctx, + onion_message, + &channel_update)) + /* No channel update. */ + return NULL; + + return patch_channel_update(ctx, take(channel_update)); +} + +/* Once we've sent it, we immediate wait for reply. */ +static struct command_result *flow_sent(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct pay_flow *flow) +{ + plugin_log(pay_plugin->plugin,LOG_DBG,"calling %s",__PRETTY_FUNCTION__); + 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 *flow_sendpay_failed(struct command *cmd, + const char *buf, + const jsmntok_t *err, + struct pay_flow *flow) +{ + plugin_log(pay_plugin->plugin,LOG_DBG,"calling %s",__PRETTY_FUNCTION__); + + struct payment *p = flow->payment; + debug_assert(p); + struct renepay * renepay = p->renepay; + debug_assert(renepay); + + /* This is a fail. */ + payment_fail(p); + + u64 errcode; + const jsmntok_t *msg = json_get_member(buf, err, "message"); + + if (!json_to_u64(buf, json_get_member(buf, err, "code"), &errcode)) + plugin_err(cmd->plugin, "Bad errcode from sendpay: %.*s", + json_tok_full_len(err), json_tok_full(buf, err)); + + if (errcode != PAY_TRY_OTHER_ROUTE) + plugin_err(cmd->plugin, "Strange error from sendpay: %.*s", + json_tok_full_len(err), json_tok_full(buf, err)); + + debug_paynote(p, + "sendpay didn't like first hop, eliminated: %.*s", + msg->end - msg->start, buf + msg->start); + + /* There is no new knowledge from this kind of failure. + * We just disable this scid. */ + tal_arr_expand(&renepay->disabled, flow->path_scids[0]); + + payflow_fail(flow); + return command_still_pending(cmd); +} + + +static struct command_result * +sendpay_flows(struct command *cmd, + struct renepay * renepay, + struct pay_flow **flows STEALS) +{ + struct payment * const p = renepay->payment; + + plugin_log(pay_plugin->plugin,LOG_DBG,"calling %s",__PRETTY_FUNCTION__); + debug_paynote(p, "Sending out batch of %zu payments", tal_count(flows)); + + for (size_t i = 0; i < tal_count(flows); i++) { + struct out_req *req; + req = jsonrpc_request_start(cmd->plugin, cmd, "sendpay", + flow_sent, flow_sendpay_failed, + flows[i]); + + json_array_start(req->js, "route"); + for (size_t j = 0; j < tal_count(flows[i]->path_nodes); j++) { + json_object_start(req->js, NULL); + json_add_node_id(req->js, "id", + &flows[i]->path_nodes[j]); + json_add_short_channel_id(req->js, "channel", + &flows[i]->path_scids[j]); + json_add_amount_msat(req->js, "amount_msat", + flows[i]->amounts[j]); + json_add_num(req->js, "direction", + flows[i]->path_dirs[j]); + json_add_u32(req->js, "delay", + flows[i]->cltv_delays[j]); + json_add_string(req->js,"style","tlv"); + json_object_end(req->js); + } + json_array_end(req->js); + + json_add_sha256(req->js, "payment_hash", &p->payment_hash); + json_add_secret(req->js, "payment_secret", p->payment_secret); + + json_add_amount_msat(req->js, "amount_msat", p->amount); + + json_add_u64(req->js, "partid", flows[i]->key.partid); + + json_add_u64(req->js, "groupid", p->groupid); + if (p->payment_metadata) + json_add_hex_talarr(req->js, "payment_metadata", + p->payment_metadata); + + /* FIXME: We don't need these three for all payments! */ + if (p->label) + json_add_string(req->js, "label", p->label); + json_add_string(req->js, "bolt11", p->invstr); + if (p->description) + json_add_string(req->js, "description", p->description); + + amount_msat_accumulate(&p->total_sent, flows[i]->amounts[0]); + amount_msat_accumulate(&p->total_delivering, + payflow_delivered(flows[i])); + + /* Flow now owned by all_flows instead of req., in this way we + * can control the destruction occurs before we remove temporary + * channels from chan_extra_map. */ + tal_steal(pay_plugin->ctx,flows[i]); + + /* Let's keep record of this flow. */ + payflow_map_add(pay_plugin->payflow_map,flows[i]); + + /* record these HTLC along the flow path */ + commit_htlc_payflow(pay_plugin->chan_extra_map,flows[i]); + + /* Remove the HTLC from the chan_extra_map after finish. */ + tal_add_destructor(flows[i], destroy_payflow); + + send_outreq(cmd->plugin, req); + } + + /* Safety check. */ + payment_assert_delivering_all(p); + + tal_free(flows); + + /* Get ready to process replies */ + renepay_settimer(renepay); + + return command_still_pending(cmd); +} + +static struct command_result *try_paying(struct command *cmd, + struct renepay *renepay, + bool first_time) +{ + struct payment * const p = renepay->payment; + plugin_log(pay_plugin->plugin,LOG_DBG,"calling %s",__PRETTY_FUNCTION__); + + // TODO(eduardo): does it make sense to have this limit on attempts? + /* I am classifying the flows in attempt cycles. */ + renepay_new_attempt(renepay); + /* We try only MAX_NUM_ATTEMPTS, then we give up. */ + if ( renepay_attempt_count(renepay) > MAX_NUM_ATTEMPTS) + { + return renepay_fail(renepay, PAY_STOPPED_RETRYING, + "Reached maximum number of attempts (%d)", + MAX_NUM_ATTEMPTS); + } + + struct amount_msat feebudget, fees_spent, remaining; + + if (time_after(time_now(), p->stop_time)) + return renepay_fail(renepay, PAY_STOPPED_RETRYING, "Timed out"); + + /* Total feebudget */ + if (!amount_msat_sub(&feebudget, p->maxspend, p->amount)) + { + plugin_err(pay_plugin->plugin, + "%s (line %d) could not substract maxspend=%s and amount=%s.", + __PRETTY_FUNCTION__, + __LINE__, + type_to_string(tmpctx, struct amount_msat, &p->maxspend), + type_to_string(tmpctx, struct amount_msat, &p->amount)); + } + + /* Fees spent so far */ + if (!amount_msat_sub(&fees_spent, p->total_sent, p->total_delivering)) + { + plugin_err(pay_plugin->plugin, + "%s (line %d) could not substract total_sent=%s and total_delivering=%s.", + __PRETTY_FUNCTION__, + __LINE__, + type_to_string(tmpctx, struct amount_msat, &p->total_sent), + type_to_string(tmpctx, struct amount_msat, &p->total_delivering)); + } + + /* Remaining fee budget. */ + if (!amount_msat_sub(&feebudget, feebudget, fees_spent)) + { + plugin_err(pay_plugin->plugin, + "%s (line %d) could not substract feebudget=%s and fees_spent=%s.", + __PRETTY_FUNCTION__, + __LINE__, + type_to_string(tmpctx, struct amount_msat, &feebudget), + type_to_string(tmpctx, struct amount_msat, &fees_spent)); + } + + /* How much are we still trying to send? */ + if (!amount_msat_sub(&remaining, p->amount, p->total_delivering)) + { + plugin_err(pay_plugin->plugin, + "%s (line %d) could not substract amount=%s and total_delivering=%s.", + __PRETTY_FUNCTION__, + __LINE__, + type_to_string(tmpctx, struct amount_msat, &p->amount), + type_to_string(tmpctx, struct amount_msat, &p->total_delivering)); + } + + // plugin_log(pay_plugin->plugin,LOG_DBG,fmt_chan_extra_map(tmpctx,pay_plugin->chan_extra_map)); + + char const * err_msg; + + /* We let this return an unlikely path, as it's better to try once + * than simply refuse. Plus, models are not truth! */ + struct pay_flow **pay_flows = get_payflows( + renepay, + remaining, feebudget, + + /* would you accept unlikely + * payments? */ + first_time, + + /* is entire payment? */ + amount_msat_eq(p->total_delivering, AMOUNT_MSAT(0)), + + &err_msg); + + // plugin_log(pay_plugin->plugin,LOG_DBG,"get_payflows produced %s",fmt_payflows(tmpctx,pay_flows)); + + /* MCF cannot find a feasible route, we stop. */ + // TODO(eduardo): alternatively we can fallback to `pay`. + if (!pay_flows) + { + return renepay_fail(renepay, PAY_ROUTE_NOT_FOUND, + "Failed to find a route, %s", + err_msg); + } + /* Now begin making payments */ + + return sendpay_flows(cmd, renepay, pay_flows); +} + +static struct command_result *listpeerchannels_done( + struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct renepay *renepay) +{ + plugin_log(pay_plugin->plugin,LOG_DBG,"calling %s",__PRETTY_FUNCTION__); + if (!uncertainty_network_update_from_listpeerchannels( + pay_plugin->chan_extra_map, + pay_plugin->my_id, + renepay, + buf, + result)) + return renepay_fail(renepay,LIGHTNINGD, + "listpeerchannels malformed: %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + // So we have all localmods data, now we apply it. Only once per + // payment. + // TODO(eduardo): check that there won't be a prob. cost associated with + // any gossmap local chan. The same way there aren't fees to pay for my + // local channels. + gossmap_apply_localmods(pay_plugin->gossmap,renepay->local_gossmods); + renepay->localmods_applied=true; + return try_paying(cmd, renepay, true); +} + + +static void destroy_payment(struct payment *p) +{ + list_del_from(&pay_plugin->payments, &p->list); +} + +static struct command_result *json_paystatus(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + const char *invstring; + struct json_stream *ret; + struct payment *p; + + if (!param(cmd, buf, params, + p_opt("invstring", param_string, &invstring), + NULL)) + return command_param_failed(); + + ret = jsonrpc_stream_success(cmd); + json_array_start(ret, "paystatus"); + + list_for_each(&pay_plugin->payments, p, list) { + if (invstring && !streq(invstring, p->invstr)) + continue; + + json_object_start(ret, NULL); + if (p->label != NULL) + json_add_string(ret, "label", p->label); + + if (p->invstr) + json_add_invstring(ret,p->invstr); + + json_add_amount_msat(ret, "amount_msat", p->amount); + json_add_sha256(ret, "payment_hash", &p->payment_hash); + json_add_node_id(ret, "destination", &p->destination); + + if (p->description) + json_add_string(ret, "description", p->description); + + json_add_timeabs(ret,"created_at",p->start_time); + json_add_u64(ret,"groupid",p->groupid); + + switch(p->status) + { + case PAYMENT_SUCCESS: + json_add_string(ret,"status","complete"); + debug_assert(p->preimage); + json_add_preimage(ret,"payment_preimage",p->preimage); + json_add_amount_msat(ret, "amount_sent_msat", p->total_sent); + + break; + case PAYMENT_FAIL: + json_add_string(ret,"status","failed"); + + break; + default: + json_add_string(ret,"status","pending"); + } + + json_array_start(ret, "notes"); + for (size_t i = 0; i < tal_count(p->paynotes); i++) + json_add_string(ret, NULL, p->paynotes[i]); + json_array_end(ret); + json_object_end(ret); + + // TODO(eduardo): maybe we should add also: + // - payment_secret? + // - payment_metadata? + // - number of parts? + } + json_array_end(ret); + + return command_finished(cmd, ret); +} + +/* Taken from ./plugins/pay.c + * + * We are interested in any prior attempts to pay this payment_hash / + * invoice so we can set the `groupid` correctly and ensure we don't + * already have a pending payment running. We also collect the summary + * about an eventual previous complete payment so we can return that + * as a no-op. */ +static struct command_result * +payment_listsendpays_previous( + struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct renepay * renepay) +{ + debug_info("calling %s",__PRETTY_FUNCTION__); + struct payment * p = renepay->payment; + + size_t i; + const jsmntok_t *t, *arr, *err; + + /* Do we have pending sendpays for the previous attempt? */ + bool pending = false; + /* Group ID of the first pending payment, this will be the one + * who's result gets replayed if we end up suspending. */ + u64 first_pending_group_id = INVALID_ID; + u64 last_pending_group_id = INVALID_ID; + u64 last_pending_partid=0; + struct amount_msat pending_sent = AMOUNT_MSAT(0), + pending_msat = AMOUNT_MSAT(0); + + /* Did a prior attempt succeed? */ + bool completed = false; + /* Metadata for a complete payment, if one exists. */ + u32 complete_parts = 0; + struct preimage complete_preimage; + struct amount_msat complete_sent = AMOUNT_MSAT(0), + complete_msat = AMOUNT_MSAT(0); + u32 complete_created_at; + + u64 last_group=INVALID_ID; + + err = json_get_member(buf, result, "error"); + if (err) + return command_fail( + cmd, LIGHTNINGD, + "Error retrieving previous pay attempts: %s", + json_strdup(tmpctx, buf, err)); + + arr = json_get_member(buf, result, "payments"); + if (!arr || arr->type != JSMN_ARRAY) + return command_fail( + cmd, LIGHTNINGD, + "Unexpected non-array result from listsendpays"); + + /* We iterate through all prior sendpays, looking for the + * latest group and remembering what its state is. */ + json_for_each_arr(i, t, arr) + { + u64 partid, groupid; + struct amount_msat this_msat, this_sent; + + const jsmntok_t *status; + + // TODO(eduardo): assuming amount_msat is always known. + json_scan(tmpctx,buf,t, + "{partid:%" + ",groupid:%" + ",amount_msat:%" + ",amount_sent_msat:%}", + JSON_SCAN(json_to_u64,&partid), + JSON_SCAN(json_to_u64,&groupid), + JSON_SCAN(json_to_msat,&this_msat), + JSON_SCAN(json_to_msat,&this_sent)); + + /* status could be completed, pending or failed */ + + + status = json_get_member(buf, t, "status"); + + if(json_tok_streq(buf,status,"failed")) + continue; + + if(json_tok_streq(buf,status,"complete")) + { + /* Now we know the payment completed. */ + completed = true; + if(!amount_msat_add(&complete_msat,complete_msat,this_msat)) + debug_err("%s (line %d) msat overflow.", + __PRETTY_FUNCTION__,__LINE__); + if(!amount_msat_add(&complete_sent,complete_sent,this_sent)) + debug_err("%s (line %d) msat overflow.", + __PRETTY_FUNCTION__,__LINE__); + json_scan(tmpctx, buf, t, + "{created_at:%" + ",payment_preimage:%}", + JSON_SCAN(json_to_u32, &complete_created_at), + JSON_SCAN(json_to_preimage, &complete_preimage)); + complete_parts ++; + } + + if(json_tok_streq(buf,status,"pending")) + { + pending = true; // there are parts pending + + if(first_pending_group_id==INVALID_ID || + last_pending_group_id==INVALID_ID) + first_pending_group_id = last_pending_group_id = groupid; + + if(groupid > last_pending_group_id) + { + last_pending_group_id = groupid; + last_pending_partid = partid; + pending_msat = AMOUNT_MSAT(0); + pending_sent = AMOUNT_MSAT(0); + } + if(groupid < first_pending_group_id) + { + first_pending_group_id = groupid; + } + if(groupid == last_pending_group_id) + { + amount_msat_accumulate(&pending_sent,this_sent); + amount_msat_accumulate(&pending_msat,this_msat); + + last_pending_partid = MAX(last_pending_partid,partid); + plugin_log(pay_plugin->plugin,LOG_DBG, + "pending deliver increased by %s", + type_to_string(tmpctx,struct amount_msat,&this_msat)); + } + + } + } + + if (completed) { + struct json_stream *ret = jsonrpc_stream_success(cmd); + json_add_preimage(ret, "payment_preimage", &complete_preimage); + json_add_string(ret, "status", "complete"); + json_add_amount_msat(ret, "amount_msat", complete_msat); + json_add_amount_msat(ret, "amount_sent_msat",complete_sent); + json_add_node_id(ret, "destination", &p->destination); + json_add_sha256(ret, "payment_hash", &p->payment_hash); + json_add_u32(ret, "created_at", complete_created_at); + json_add_num(ret, "parts", complete_parts); + + /* This payment was already completed, we don't keep record of + * it twice. */ + renepay->payment = tal_free(renepay->payment); + + return command_finished(cmd, ret); + } else if (pending) { + p->groupid = last_pending_group_id; + renepay->next_partid = last_pending_partid+1; + + p->total_sent = pending_sent; + p->total_delivering = pending_msat; + + plugin_log(pay_plugin->plugin,LOG_DBG, + "There are pending sendpays to this invoice. " + "groupid = %ld, " + "delivering = %s, " + "last_partid = %ld", + last_pending_group_id, + type_to_string(tmpctx,struct amount_msat,&p->total_delivering), + last_pending_partid); + + if( first_pending_group_id != last_pending_group_id) + { + /* At least two pending groups for the same invoice, + * this is weird, we better stop. */ + renepay->payment = tal_free(renepay->payment); + return renepay_fail(renepay, PAY_IN_PROGRESS, + "Payment is pending by some other request."); + } + if(amount_msat_greater_eq(p->total_delivering,p->amount)) + { + /* Pending payment already pays the full amount, we + * better stop. */ + renepay->payment = tal_free(renepay->payment); + return renepay_fail(renepay, PAY_IN_PROGRESS, + "Payment is pending with full amount already commited"); + } + }else + { + p->groupid = (last_group==INVALID_ID ? 1 : (last_group+1)) ; + renepay->next_partid=1; + } + + + struct out_req *req; + /* Get local capacities... */ + req = jsonrpc_request_start(cmd->plugin, cmd, "listpeerchannels", + listpeerchannels_done, + listpeerchannels_done, renepay); + return send_outreq(cmd->plugin, req); +} + +static struct command_result *json_pay(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + const char *invstr; + const char *label; + const char *description; + struct sha256 * local_offer_id; + u64 invexpiry; + struct amount_msat *msat, *invmsat; + struct amount_msat *maxfee; + u64 *riskfactor_millionths; + u32 *maxdelay; + u64 *base_fee_penalty; + u64 *prob_cost_factor; + u64 *min_prob_success_millionths; + u32 *retryfor; + +#if DEVELOPER + bool *use_shadow; +#endif + + if (!param(cmd, buf, params, + p_req("invstring", param_string, &invstr), + p_opt("amount_msat", param_msat, &msat), + p_opt("maxfee", param_msat, &maxfee), + + // MCF parameters + // TODO(eduardo): are these parameters read correctly? + p_opt_def("base_fee_penalty", param_millionths, &base_fee_penalty,10), + p_opt_def("prob_cost_factor", param_millionths, &prob_cost_factor,10), + p_opt_def("min_prob_success", param_millionths, + &min_prob_success_millionths,100000),// default is 10% + + p_opt_def("riskfactor", param_millionths,&riskfactor_millionths,1), + + p_opt_def("maxdelay", param_number, &maxdelay, + /* We're initially called to probe usage, before init! */ + pay_plugin ? pay_plugin->maxdelay_default : 0), + + + p_opt_def("retry_for", param_number, &retryfor, 60), // 60 seconds + p_opt("localofferid", param_sha256, &local_offer_id), + p_opt("description", param_string, &description), + p_opt("label", param_string, &label), +#if DEVELOPER + p_opt_def("use_shadow", param_bool, &use_shadow, true), +#endif + NULL)) + return command_param_failed(); + + /* renepay is bound to the command, if the command finishes renepay is + * freed. */ + struct renepay * renepay = renepay_new(cmd); + tal_add_destructor2(renepay, + renepay_cleanup, + pay_plugin->gossmap); + struct payment * p = renepay->payment; + + p->invstr = tal_steal(p,invstr); + p->description = tal_steal(p,description); + p->label = tal_steal(p,label); + p->local_offer_id = tal_steal(p,local_offer_id); + + p->base_fee_penalty = *base_fee_penalty; + base_fee_penalty = tal_free(base_fee_penalty); + + p->prob_cost_factor = *prob_cost_factor; + prob_cost_factor = tal_free(prob_cost_factor); + + p->min_prob_success = *min_prob_success_millionths/1e6; + min_prob_success_millionths = tal_free(min_prob_success_millionths); + + p->delay_feefactor = *riskfactor_millionths/1e6; + riskfactor_millionths = tal_free(riskfactor_millionths); + + p->maxdelay = *maxdelay; + maxdelay = tal_free(maxdelay); + + /* We inmediately add this payment to the payment list. */ + tal_steal(pay_plugin->ctx,p); + list_add_tail(&pay_plugin->payments, &p->list); + tal_add_destructor(p, destroy_payment); + + plugin_log(pay_plugin->plugin,LOG_DBG,"Starting renepay"); + bool gossmap_changed = gossmap_refresh(pay_plugin->gossmap, NULL); + + if (pay_plugin->gossmap == NULL) + plugin_err(pay_plugin->plugin, "Failed to refresh gossmap: %s", + strerror(errno)); + + p->start_time = time_now(); + p->stop_time = timeabs_add(p->start_time, time_from_sec(*retryfor)); + tal_free(retryfor); + + bool invstr_is_b11=false; + if (!bolt12_has_prefix(p->invstr)) { + struct bolt11 *b11; + char *fail; + + b11 = + bolt11_decode(tmpctx, p->invstr, plugin_feature_set(cmd->plugin), + p->description, chainparams, &fail); + if (b11 == NULL) + return renepay_fail(renepay, JSONRPC2_INVALID_PARAMS, + "Invalid bolt11: %s", fail); + invstr_is_b11=true; + + invmsat = b11->msat; + invexpiry = b11->timestamp + b11->expiry; + + p->destination = b11->receiver_id; + p->payment_hash = b11->payment_hash; + p->payment_secret = + tal_dup_or_null(p, struct secret, b11->payment_secret); + if (b11->metadata) + p->payment_metadata = tal_dup_talarr(p, u8, b11->metadata); + else + p->payment_metadata = NULL; + + + p->final_cltv = b11->min_final_cltv_expiry; + /* Sanity check */ + if (feature_offered(b11->features, OPT_VAR_ONION) && + !b11->payment_secret) + return command_fail( + cmd, JSONRPC2_INVALID_PARAMS, + "Invalid bolt11:" + " sets feature var_onion with no secret"); + /* BOLT #11: + * A reader: + *... + * - MUST check that the SHA2 256-bit hash in the `h` field + * exactly matches the hashed description. + */ + if (!b11->description) { + if (!b11->description_hash) { + return renepay_fail(renepay, + JSONRPC2_INVALID_PARAMS, + "Invalid bolt11: missing description"); + } + if (!p->description) + return renepay_fail(renepay, + JSONRPC2_INVALID_PARAMS, + "bolt11 uses description_hash, but you did not provide description parameter"); + } + } else { + // TODO(eduardo): check this, compare with `pay` + const struct tlv_invoice *b12; + char *fail; + b12 = invoice_decode(tmpctx, p->invstr, strlen(p->invstr), + plugin_feature_set(cmd->plugin), + chainparams, &fail); + if (b12 == NULL) + return renepay_fail(renepay, JSONRPC2_INVALID_PARAMS, + "Invalid bolt12: %s", fail); + if (!pay_plugin->exp_offers) + return renepay_fail(renepay, JSONRPC2_INVALID_PARAMS, + "experimental-offers disabled"); + + if (!b12->offer_node_id) + return renepay_fail(renepay, JSONRPC2_INVALID_PARAMS, + "invoice missing offer_node_id"); + if (!b12->invoice_payment_hash) + return renepay_fail(renepay, JSONRPC2_INVALID_PARAMS, + "invoice missing payment_hash"); + if (!b12->invoice_created_at) + return renepay_fail(renepay, JSONRPC2_INVALID_PARAMS, + "invoice missing created_at"); + if (b12->invoice_amount) { + invmsat = tal(cmd, struct amount_msat); + *invmsat = amount_msat(*b12->invoice_amount); + } else + invmsat = NULL; + + node_id_from_pubkey(&p->destination, b12->offer_node_id); + p->payment_hash = *b12->invoice_payment_hash; + if (b12->invreq_recurrence_counter && !p->label) + return renepay_fail( + renepay, JSONRPC2_INVALID_PARAMS, + "recurring invoice requires a label"); + /* FIXME payment_secret should be signature! */ + { + struct sha256 merkle; + + p->payment_secret = tal(p, struct secret); + merkle_tlv(b12->fields, &merkle); + memcpy(p->payment_secret, &merkle, sizeof(merkle)); + BUILD_ASSERT(sizeof(*p->payment_secret) == + sizeof(merkle)); + } + p->payment_metadata = NULL; + /* FIXME: blinded paths! */ + p->final_cltv = 18; + /* BOLT-offers #12: + * - if `relative_expiry` is present: + * - MUST reject the invoice if the current time since + * 1970-01-01 UTC is greater than `created_at` plus + * `seconds_from_creation`. + * - otherwise: + * - MUST reject the invoice if the current time since + * 1970-01-01 UTC is greater than `created_at` plus + * 7200. + */ + if (b12->invoice_relative_expiry) + invexpiry = *b12->invoice_created_at + *b12->invoice_relative_expiry; + else + invexpiry = *b12->invoice_created_at + BOLT12_DEFAULT_REL_EXPIRY; + } + + if (node_id_eq(&pay_plugin->my_id, &p->destination)) + return renepay_fail(renepay, JSONRPC2_INVALID_PARAMS, + "This payment is destined for ourselves. " + "Self-payments are not supported"); + + + // set the payment amount + if (invmsat) { + // amount is written in the invoice + if (msat) { + return renepay_fail(renepay, JSONRPC2_INVALID_PARAMS, + "amount_msat parameter unnecessary"); + } + p->amount = *invmsat; + tal_free(invmsat); + } else { + // amount is not written in the invoice + if (!msat) { + return renepay_fail(renepay, JSONRPC2_INVALID_PARAMS, + "amount_msat parameter required"); + } + p->amount = *msat; + tal_free(msat); + } + + /* Default max fee is 5 sats, or 0.5%, whichever is *higher* */ + if (!maxfee) { + struct amount_msat fee = amount_msat_div(p->amount, 200); + if (amount_msat_less(fee, AMOUNT_MSAT(5000))) + fee = AMOUNT_MSAT(5000); + maxfee = tal_dup(tmpctx, struct amount_msat, &fee); + } + + if (!amount_msat_add(&p->maxspend, p->amount, *maxfee)) { + return renepay_fail( + renepay, JSONRPC2_INVALID_PARAMS, + "Overflow when computing fee budget, fee far too high."); + } + tal_free(maxfee); + + if (time_now().ts.tv_sec > invexpiry) + return renepay_fail(renepay, PAY_INVOICE_EXPIRED, "Invoice expired"); + + + /* To construct the uncertainty network we need to perform the following + * steps: + * 1. check that there is a 1-to-1 map between channels in gossmap + * and the uncertainty network. We call `uncertainty_network_update` + * + * 2. add my local channels that could be private. + * We call `update_uncertainty_network_from_listpeerchannels`. + * + * 3. add hidden/private channels listed in the routehints. + * We call `uncertainty_network_add_routehints`. + * + * 4. check the uncertainty network invariants. + * */ + if(gossmap_changed) + uncertainty_network_update(pay_plugin->gossmap, + pay_plugin->chan_extra_map); + + + // TODO(eduardo): are there route hints for B12? + // Add any extra hidden channel revealed by the routehints to the uncertainty network. + if(invstr_is_b11) + uncertainty_network_add_routehints(pay_plugin->chan_extra_map,renepay); + + if(!uncertainty_network_check_invariants(pay_plugin->chan_extra_map)) + plugin_log(pay_plugin->plugin, + LOG_BROKEN, + "uncertainty network invariants are violated"); + + /* Next, request listsendpays for previous payments that use the same + * hash. */ + struct out_req *req + = jsonrpc_request_start(cmd->plugin, cmd, "listsendpays", + payment_listsendpays_previous, + payment_listsendpays_previous, renepay); + + json_add_sha256(req->js, "payment_hash", &p->payment_hash); + return send_outreq(cmd->plugin, req); + + // TODO(eduardo): + // - get time since last payment, + // - forget a portion of the bounds + // - note that if sufficient time has passed, then we would forget + // everything use TIMER_FORGET_SEC. +} + +static void handle_sendpay_failure_renepay( + struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct renepay *renepay, + struct pay_flow *flow) +{ + debug_assert(renepay); + debug_assert(flow); + struct payment *p = renepay->payment; + debug_assert(p); + + u64 errcode; + if (!json_to_u64(buf, json_get_member(buf, result, "code"), &errcode)) + { + plugin_log(pay_plugin->plugin,LOG_BROKEN, + "Failed to get code from sendpay_failure notification" + ", received json: %.*s", + json_tok_full_len(result), + json_tok_full(buf,result)); + return; + } + const jsmntok_t *msgtok = json_get_member(buf, result, "message"); + const char *message; + if(msgtok) + message = tal_fmt(tmpctx,"%.*s", + msgtok->end - msgtok->start, + buf + msgtok->start); + else + message = "[message missing from sendpay_failure notification]"; + + switch(errcode) + { + case PAY_UNPARSEABLE_ONION: + debug_paynote(p, "Unparsable onion reply on route %s", + flow_path_to_str(tmpctx, flow)); + goto unhandleable; + case PAY_TRY_OTHER_ROUTE: + break; + case PAY_DESTINATION_PERM_FAIL: + renepay_fail(renepay,errcode, + "Got a final failure from destination"); + return; + default: + renepay_fail(renepay,errcode, + "Unexpected errocode from sendpay_failure: %.*s", + json_tok_full_len(result), + json_tok_full(buf,result)); + return; + } + + const jsmntok_t* datatok = json_get_member(buf, result, "data"); + + if(!datatok) + { + plugin_err(pay_plugin->plugin, + "Failed to get data from sendpay_failure notification" + ", received json: %.*s", + json_tok_full_len(result), + json_tok_full(buf,result)); + } + + + /* OK, we expect an onion error reply. */ + u32 erridx; + const jsmntok_t * erridxtok = json_get_member(buf, datatok, "erring_index"); + if (!erridxtok || !json_to_u32(buf, erridxtok, &erridx)) + { + debug_paynote(p, "Missing erring_index reply on route %s", + flow_path_to_str(tmpctx, flow)); + plugin_log(pay_plugin->plugin,LOG_DBG, + "%s (line %d) missing erring_index " + "on request %.*s", + __PRETTY_FUNCTION__,__LINE__, + json_tok_full_len(result), + json_tok_full(buf,result)); + goto unhandleable; + } + + struct short_channel_id errscid; + const jsmntok_t *errchantok = json_get_member(buf, datatok, "erring_channel"); + if(!errchantok || !json_to_short_channel_id(buf, errchantok, &errscid)) + { + debug_paynote(p, "Missing erring_channel reply on route %s", + flow_path_to_str(tmpctx, flow)); + goto unhandleable; + } + + if (erridxpath_scids) + && !short_channel_id_eq(&errscid, &flow->path_scids[erridx])) + { + debug_paynote(p, + "erring_index (%d) does not correspond" + "to erring_channel (%s) on route %s", + erridx, + type_to_string(tmpctx,struct short_channel_id,&errscid), + flow_path_to_str(tmpctx,flow)); + goto unhandleable; + } + + u32 onionerr; + const jsmntok_t *failcodetok = json_get_member(buf, datatok, "failcode"); + if(!failcodetok || !json_to_u32(buf, failcodetok, &onionerr)) + { + // TODO(eduardo): I wonder which error code should I show the + // user in this case? + renepay_fail(renepay,LIGHTNINGD, + "Failed to get failcode from sendpay_failure notification" + ", received json: %.*s", + json_tok_full_len(result), + json_tok_full(buf,result)); + return; + } + + debug_paynote(p, + "onion error %s from node #%u %s: %s", + onion_wire_name(onionerr), + erridx, + type_to_string(tmpctx, struct short_channel_id, &errscid), + message); + + const jsmntok_t *rawoniontok = json_get_member(buf, datatok, "raw_message"); + if(!rawoniontok) + goto unhandleable; + + switch ((enum onion_wire)onionerr) { + /* These definitely mean eliminate channel */ + case WIRE_PERMANENT_CHANNEL_FAILURE: + case WIRE_REQUIRED_CHANNEL_FEATURE_MISSING: + /* FIXME: lnd returns this for disconnected peer, so don't disable perm! */ + case WIRE_UNKNOWN_NEXT_PEER: + case WIRE_CHANNEL_DISABLED: + /* These mean node is weird, but we eliminate channel here too */ + case WIRE_INVALID_REALM: + case WIRE_TEMPORARY_NODE_FAILURE: + case WIRE_PERMANENT_NODE_FAILURE: + case WIRE_REQUIRED_NODE_FEATURE_MISSING: + /* These shouldn't happen, but eliminate channel */ + case WIRE_INVALID_ONION_VERSION: + case WIRE_INVALID_ONION_HMAC: + case WIRE_INVALID_ONION_KEY: + case WIRE_INVALID_ONION_PAYLOAD: + case WIRE_INVALID_ONION_BLINDING: + case WIRE_EXPIRY_TOO_FAR: + debug_paynote(p, "we're removing scid %s", + type_to_string(tmpctx,struct short_channel_id,&errscid)); + tal_arr_expand(&renepay->disabled, errscid); + return; + + /* These can be fixed (maybe) by applying the included channel_update */ + case WIRE_AMOUNT_BELOW_MINIMUM: + case WIRE_FEE_INSUFFICIENT: + case WIRE_INCORRECT_CLTV_EXPIRY: + case WIRE_EXPIRY_TOO_SOON: + plugin_log(pay_plugin->plugin,LOG_DBG,"sendpay_failure, apply channel_update"); + /* FIXME: Check scid! */ + // TODO(eduardo): check + const u8 *update = channel_update_from_onion_error(tmpctx, buf, rawoniontok); + if (update) + { + submit_update(cmd, flow, update, errscid); + return; + } + + debug_paynote(p, "missing an update, so we're removing scid %s", + type_to_string(tmpctx,struct short_channel_id,&errscid)); + tal_arr_expand(&renepay->disabled, errscid); + return; + + case WIRE_TEMPORARY_CHANNEL_FAILURE: + case WIRE_MPP_TIMEOUT: + return; + + /* These are from the final distination: fail */ + case WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS: + case WIRE_FINAL_INCORRECT_CLTV_EXPIRY: + case WIRE_FINAL_INCORRECT_HTLC_AMOUNT: + debug_paynote(p,"final destination failure"); + renepay_fail(renepay,errcode, + "Destination said %s: %s", + onion_wire_name(onionerr), + message); + return; + } + + debug_assert(erridx<=tal_count(flow->path_nodes)); + + if(erridx == tal_count(flow->path_nodes)) + { + debug_paynote(p,"unkown onion error code %u, fatal", + onionerr); + renepay_fail(renepay,errcode, + "Destination gave unknown error code %u: %s", + onionerr,message); + return; + }else + { + debug_paynote(p,"unkown onion error code %u, removing scid %s", + onionerr, + type_to_string(tmpctx,struct short_channel_id,&errscid)); + tal_arr_expand(&renepay->disabled, errscid); + return; + } + unhandleable: + // TODO(eduardo): check + handle_unhandleable_error(renepay, flow, ""); +} + +static void handle_sendpay_failure_flow( + struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct pay_flow *flow) +{ + // TODO(eduardo): review with Rusty the level of severity of the + // different cases of error below. + debug_assert(flow); + + struct payment * const p = flow->payment; + payment_fail(p); + + u64 errcode; + if (!json_to_u64(buf, json_get_member(buf, result, "code"), &errcode)) + { + plugin_log(pay_plugin->plugin,LOG_BROKEN, + "Failed to get code from sendpay_failure notification" + ", received json: %.*s", + json_tok_full_len(result), + json_tok_full(buf,result)); + return; + } + const jsmntok_t *msgtok = json_get_member(buf, result, "message"); + const char *message; + if(msgtok) + message = tal_fmt(tmpctx,"%.*s", + msgtok->end - msgtok->start, + buf + msgtok->start); + else + message = "[message missing from sendpay_failure notification]"; + + if(errcode!=PAY_TRY_OTHER_ROUTE) + return; + + const jsmntok_t* datatok = json_get_member(buf, result, "data"); + if(!datatok) + { + plugin_err(pay_plugin->plugin, + "Failed to get data from sendpay_failure notification" + ", received json: %.*s", + json_tok_full_len(result), + json_tok_full(buf,result)); + } + + /* OK, we expect an onion error reply. */ + u32 erridx; + const jsmntok_t * erridxtok = json_get_member(buf, datatok, "erring_index"); + if (!erridxtok || !json_to_u32(buf, erridxtok, &erridx)) + { + plugin_log(pay_plugin->plugin,LOG_BROKEN, + "Failed to get erring_index from sendpay_failure notification" + ", received json: %.*s", + json_tok_full_len(result), + json_tok_full(buf,result)); + return; + } + + struct short_channel_id errscid; + const jsmntok_t *errchantok = json_get_member(buf, datatok, "erring_channel"); + if(!errchantok || !json_to_short_channel_id(buf, errchantok, &errscid)) + { + plugin_log(pay_plugin->plugin,LOG_BROKEN, + "Failed to get erring_channel from sendpay_failure notification" + ", received json: %.*s", + json_tok_full_len(result), + json_tok_full(buf,result)); + return; + } + + if (erridxpath_scids) + && !short_channel_id_eq(&errscid, &flow->path_scids[erridx])) + { + plugin_err(pay_plugin->plugin, + "Erring channel %u/%zu was %s not %s (path %s)", + erridx, tal_count(flow->path_scids), + type_to_string(tmpctx, + struct short_channel_id, + &errscid), + type_to_string(tmpctx, + struct short_channel_id, + &flow->path_scids[erridx]), + flow_path_to_str(tmpctx, flow)); + return; + } + + + u32 onionerr; + const jsmntok_t *failcodetok = json_get_member(buf, datatok, "failcode"); + if(!failcodetok || !json_to_u32(buf, failcodetok, &onionerr)) + { + plugin_log(pay_plugin->plugin,LOG_BROKEN, + "Failed to get failcode from sendpay_failure notification" + ", received json: %.*s", + json_tok_full_len(result), + json_tok_full(buf,result)); + return; + + } + + plugin_log(pay_plugin->plugin,LOG_UNUSUAL, + "onion error %s from node #%u %s: " + "%s", + onion_wire_name(onionerr), + erridx, + type_to_string(tmpctx, struct short_channel_id, &errscid), + message); + + /* we know that all channels before erridx where able to commit to this payment */ + uncertainty_network_channel_can_send( + pay_plugin->chan_extra_map, + flow, + erridx); + + /* Insufficient funds! */ + if((enum onion_wire)onionerr == WIRE_TEMPORARY_CHANNEL_FAILURE) + { + plugin_log(pay_plugin->plugin,LOG_DBG, + "sendpay_failure says insufficient funds!"); + + chan_extra_cannot_send(p,pay_plugin->chan_extra_map, + flow->path_scids[erridx], + flow->path_dirs[erridx], + /* This channel can't send all that was + * commited in HTLCs. + * Had we removed the commited amount then + * we would have to put here flow->amounts[erridx]. */ + AMOUNT_MSAT(0)); + } +} + +// TODO(eduardo): if I subscribe to a shutdown notification, the plugin takes +// forever to close and eventually it gets killed by force. +// static struct command_result *notification_shutdown(struct command *cmd, +// const char *buf, +// const jsmntok_t *params) +// { +// /* TODO(eduardo): +// * 1. at shutdown the `struct plugin *p` is not freed, +// * 2. `memleak_check` is called before we have the chance to get this +// * notification. */ +// // plugin_log(pay_plugin->plugin,LOG_DBG,"received shutdown notification, freeing data."); +// pay_plugin->ctx = tal_free(pay_plugin->ctx); +// return notification_handled(cmd); +// } +static struct command_result *notification_sendpay_success( + struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + struct pay_flow *flow = NULL; + const jsmntok_t *resulttok = json_get_member(buf,params,"sendpay_success"); + if(!resulttok) + debug_err("Failed to get result from sendpay_success notification" + ", received json: %.*s", + json_tok_full_len(params), + json_tok_full(buf,params)); + + // 1. generate the key of this payflow + struct payflow_key key; + key.payment_hash = tal(tmpctx,struct sha256); + + const jsmntok_t *parttok = json_get_member(buf,resulttok,"partid"); + if(!parttok || !json_to_u64(buf,parttok,&key.partid)) + { + // No partid, is this a single-path payment? + key.partid = 0; + // debug_err("Failed to get partid from sendpay_success notification" + // ", received json: %.*s", + // json_tok_full_len(params), + // json_tok_full(buf,params)); + } + const jsmntok_t *grouptok = json_get_member(buf,resulttok,"groupid"); + if(!grouptok || !json_to_u64(buf,grouptok,&key.groupid)) + debug_err("Failed to get groupid from sendpay_success notification" + ", received json: %.*s", + json_tok_full_len(params), + json_tok_full(buf,params)); + + const jsmntok_t *hashtok = json_get_member(buf,resulttok,"payment_hash"); + if(!hashtok || !json_to_sha256(buf,hashtok,key.payment_hash)) + debug_err("Failed to get payment_hash from sendpay_success notification" + ", received json: %.*s", + json_tok_full_len(params), + json_tok_full(buf,params)); + + plugin_log(pay_plugin->plugin,LOG_DBG, + "I received a sendpay_success with key %s", + fmt_payflow_key(tmpctx,&key)); + + // 2. is this payflow recorded in renepay? + flow = payflow_map_get(pay_plugin->payflow_map,key); + if(!flow) + { + plugin_log(pay_plugin->plugin,LOG_DBG, + "sendpay_success does not correspond to a renepay attempt, %s", + fmt_payflow_key(tmpctx,&key)); + goto done; + } + + // 3. mark as success + struct payment * const p = flow->payment; + debug_assert(p); + + payment_success(p); + + const jsmntok_t *preimagetok + = json_get_member(buf, resulttok, "payment_preimage"); + struct preimage preimage; + + if (!preimagetok || !json_to_preimage(buf, preimagetok,&preimage)) + debug_err("Failed to get payment_preimage from sendpay_success notification" + ", received json: %.*s", + json_tok_full_len(params), + json_tok_full(buf,params)); + + p->preimage = tal_dup_or_null(p,struct preimage,&preimage); + + // 4. update information and release pending HTLCs + uncertainty_network_flow_success(pay_plugin->chan_extra_map,flow); + + done: + tal_free(flow); + return notification_handled(cmd); +} +static struct command_result *notification_sendpay_failure( + struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + struct pay_flow *flow = NULL; + + const jsmntok_t *resulttok = json_get_member(buf,params,"sendpay_failure"); + if(!resulttok) + debug_err("Failed to get result from sendpay_failure notification" + ", received json: %.*s", + json_tok_full_len(params), + json_tok_full(buf,params)); + + const jsmntok_t *datatok = json_get_member(buf,resulttok,"data"); + if(!datatok) + debug_err("Failed to get data from sendpay_failure notification" + ", received json: %.*s", + json_tok_full_len(params), + json_tok_full(buf,params)); + + + // 1. generate the key of this payflow + struct payflow_key key; + key.payment_hash = tal(tmpctx,struct sha256); + + const jsmntok_t *parttok = json_get_member(buf,datatok,"partid"); + if(!parttok || !json_to_u64(buf,parttok,&key.partid)) + { + // No partid, is this a single-path payment? + key.partid = 0; + } + const jsmntok_t *grouptok = json_get_member(buf,datatok,"groupid"); + if(!grouptok || !json_to_u64(buf,grouptok,&key.groupid)) + debug_err("Failed to get groupid from sendpay_failure notification" + ", received json: %.*s", + json_tok_full_len(params), + json_tok_full(buf,params)); + + const jsmntok_t *hashtok = json_get_member(buf,datatok,"payment_hash"); + if(!hashtok || !json_to_sha256(buf,hashtok,key.payment_hash)) + debug_err("Failed to get payment_hash from sendpay_failure notification" + ", received json: %.*s", + json_tok_full_len(params), + json_tok_full(buf,params)); + + plugin_log(pay_plugin->plugin,LOG_DBG, + "I received a sendpay_failure with key %s", + fmt_payflow_key(tmpctx,&key)); + + // 2. is this payflow recorded in renepay? + flow = payflow_map_get(pay_plugin->payflow_map,key); + if(!flow) + { + plugin_log(pay_plugin->plugin,LOG_DBG, + "sendpay_failure does not correspond to a renepay attempt, %s", + fmt_payflow_key(tmpctx,&key)); + goto done; + } + + // 3. process failure + handle_sendpay_failure_flow(cmd,buf,resulttok,flow); + + // there is possibly a pending renepay command for this flow + struct renepay * const renepay = flow->payment->renepay; + + if(renepay) + handle_sendpay_failure_renepay(cmd,buf,resulttok,renepay,flow); + + done: + if(flow) payflow_fail(flow); + return notification_handled(cmd); +} + +static const struct plugin_command commands[] = { + { + "renepaystatus", + "payment", + "Detail status of attempts to pay {bolt11}, or all", + "Covers both old payments and current ones.", + json_paystatus + }, + { + "renepay", + "payment", + "Send payment specified by {invstring}", + "Attempt to pay an invoice.", + json_pay + }, +}; + +static const struct plugin_notification notifications[] = { + // { + // "shutdown", + // notification_shutdown, + // }, + { + "sendpay_success", + notification_sendpay_success, + }, + { + "sendpay_failure", + notification_sendpay_failure, + } +}; + +int main(int argc, char *argv[]) +{ + setup_locale(); + plugin_main( + argv, + init, + PLUGIN_RESTARTABLE, + /* init_rpc */ true, + /* features */ NULL, + commands, ARRAY_SIZE(commands), + notifications, ARRAY_SIZE(notifications), + /* hooks */ NULL, 0, + /* notification topics */ NULL, 0, + plugin_option("renepay-debug-mcf", "flag", + "Enable renepay MCF debug info.", + flag_option, &pay_plugin->debug_mcf), + plugin_option("renepay-debug-payflow", "flag", + "Enable renepay payment flows debug info.", + flag_option, &pay_plugin->debug_payflow), + NULL); + + // TODO(eduardo): I think this is actually never executed + tal_free(pay_plugin->ctx); + return 0; +} diff --git a/plugins/renepay/pay.h b/plugins/renepay/pay.h new file mode 100644 index 000000000..2cc7cbb42 --- /dev/null +++ b/plugins/renepay/pay.h @@ -0,0 +1,117 @@ +#ifndef LIGHTNING_PLUGINS_RENEPAY_PAY_H +#define LIGHTNING_PLUGINS_RENEPAY_PAY_H +#include "config.h" +#include +#include +#include +#include +#include + +// TODO(eduardo): renepaystatus should be similar to paystatus + +// TODO(eduardo): MCF should consider pending HTLCs occupy some capacity in the +// routing channels. + +// TODO(eduardo): check a problem with param_millionths(), if I input an integer +// should or shouldn't be multiplied by 10^6? +// TODO(eduardo): add an option entry for maxfeepercent +// TODO(eduardo): write a man entry for renepay +// TODO(eduardo): check if paynotes are meaningful +// TODO(eduardo): remove assertions, introduce LOG_BROKEN messages + +#define MAX_NUM_ATTEMPTS 10 + +/* Time lapse used to wait for failed sendpays before try_paying. */ +#define TIMER_COLLECT_FAILURES_MSEC 250 + +/* Knowledge is proportionally decreased with time up to TIMER_FORGET_SEC when + * we forget everything. */ +#define TIMER_FORGET_SEC 3600 + +// TODO(eduardo): Test ideas +// - make a payment to a node that is hidden behind private channels, check that +// private channels are removed from the gossmap and chan_extra_map +// - one payment route hangs, and the rest keep waiting, eventually all MPP +// should timeout and we retry excluding the unresponsive path (are we able to +// identify it?) +// - a particular route fails because fees are wrong, we update the gossip +// information and redo the path. +// - a MPP in which several parts have a common intermediary node +// source -MANY- o -MANY- dest +// - a MPP in which several parts have a common intermediary channel +// source -MANY- o--o -MANY- dest +// - a payment with a direct channel to the destination +// - payment failures: +// - destination is not in the gossmap +// - destination is offline +// - with current knowledge there is no flow solution to destination + +/* Our convenient global data, here in one place. */ +struct pay_plugin { + /* From libplugin */ + struct plugin *plugin; + + /* Public key of this node. */ + struct node_id my_id; + + /* Map of gossip. */ + struct gossmap *gossmap; + + /* Settings for maxdelay */ + unsigned int maxdelay_default; + + /* Offers support */ + bool exp_offers; + + /* All the struct payment */ + struct list_head payments; + + /* Per-channel metadata: some persists between payments */ + struct chan_extra_map *chan_extra_map; + + /* Pending senpays. */ + struct payflow_map * payflow_map; + + bool debug_mcf; + bool debug_payflow; + + /* I'll allocate all global (controlled by pay_plugin) variables tied to + * this tal_t. */ + tal_t *ctx; + // TODO(eduardo): pending flows have HTLCs (in-flight) liquidity + // attached that is reflected in the uncertainty network. When + // waitsendpay returns either fail or success that flow is destroyed and + // the liquidity is restored. A payment command could end before all + // flows are destroyed, therefore it is important to delegate the + // ownership of the waitsendpay request to pay_plugin->ctx so that the + // request is kept alive. One more thing: to double check that flows are + // not accumulating ad-infinitum I would insert them into a data + // structure here so that once in a while a timer kicks and verifies the + // list of pending flows. + // TODO(eduardo): notice that pending attempts performed with another + // pay plugin are not considered by the uncertainty network in renepay, + // it would be nice if listsendpay would give us the route of pending + // sendpays. + /* Timers. */ + struct plugin_timer *rexmit_timer; +}; + +/* Set in init */ +extern struct pay_plugin * const pay_plugin; + +/* Accumulate or panic on overflow */ +#define amount_msat_accumulate(dst, src) \ + amount_msat_accumulate_((dst), (src), stringify(dst), stringify(src)) +#define amount_msat_reduce(dst, src) \ + amount_msat_reduce_((dst), (src), stringify(dst), stringify(src)) + +void amount_msat_accumulate_(struct amount_msat *dst, + struct amount_msat src, + const char *dstname, + const char *srcname); +void amount_msat_reduce_(struct amount_msat *dst, + struct amount_msat src, + const char *dstname, + const char *srcname); + +#endif /* LIGHTNING_PLUGINS_RENEPAY_PAY_H */ diff --git a/plugins/renepay/pay_flow.c b/plugins/renepay/pay_flow.c new file mode 100644 index 000000000..b20656463 --- /dev/null +++ b/plugins/renepay/pay_flow.c @@ -0,0 +1,633 @@ +/* Routines to get suitable pay_flow array from pay constraints */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* BOLT #7: + * + * If a route is computed by simply routing to the intended recipient and summing + * the `cltv_expiry_delta`s, then it's possible for intermediate nodes to guess + * their position in the route. Knowing the CLTV of the HTLC, the surrounding + * network topology, and the `cltv_expiry_delta`s gives an attacker a way to guess + * the intended recipient. Therefore, it's highly desirable to add a random offset + * to the CLTV that the intended recipient will receive, which bumps all CLTVs + * along the route. + * + * In order to create a plausible offset, the origin node MAY start a limited + * random walk on the graph, starting from the intended recipient and summing the + * `cltv_expiry_delta`s, and use the resulting sum as the offset. + * This effectively creates a _shadow route extension_ to the actual route and + * provides better protection against this attack vector than simply picking a + * random offset would. + */ + +/* There's little benefit in doing this per-flow, since you can + * correlate flows so trivially, but it's good practice for when we + * have PTLCs and that's not true. */ + +#define MAX_SHADOW_LEN 3 + +/* Returns CLTV, and fills in *shadow_fee, based on extending the path */ +static u32 shadow_one_flow(const struct gossmap *gossmap, + const struct flow *f, + struct amount_msat *shadow_fee) +{ + size_t numpath = tal_count(f->amounts); + struct amount_msat amount = f->amounts[numpath-1]; + struct gossmap_node *n; + size_t hop; + struct gossmap_chan *chans[MAX_SHADOW_LEN]; + int dirs[MAX_SHADOW_LEN]; + u32 shadow_delay = 0; + + /* Start at end of path */ + n = gossmap_nth_node(gossmap, f->path[numpath-1], !f->dirs[numpath-1]); + + /* We only create shadow for extra CLTV delays, *not* for + * amounts. This is because with MPP our amounts are random + * looking already. */ + for (hop = 0; hop < MAX_SHADOW_LEN && pseudorand(1); hop++) { + /* Try for a believable channel up to 10 times, then stop */ + for (size_t i = 0; i < 10; i++) { + struct amount_sat cap; + chans[hop] = gossmap_nth_chan(gossmap, n, pseudorand(n->num_chans), + &dirs[hop]); + if (!gossmap_chan_set(chans[hop], dirs[hop]) + || !gossmap_chan_get_capacity(gossmap, chans[hop], &cap) + /* This test is approximate, since amount would differ */ + || amount_msat_less_sat(amount, cap)) { + chans[hop] = NULL; + continue; + } + } + if (!chans[hop]) + break; + + shadow_delay += chans[hop]->half[dirs[hop]].delay; + n = gossmap_nth_node(gossmap, chans[hop], !dirs[hop]); + } + + /* If we were actually trying to get amount to end of shadow, + * what would we be paying to the "intermediary" node (real dest) */ + for (int i = (int)hop - 1; i >= 0; i--) + if (!amount_msat_add_fee(&amount, + chans[i]->half[dirs[i]].base_fee, + chans[i]->half[dirs[i]].proportional_fee)) + /* Ignore: treats impossible event as zero fee. */ + ; + + /* Shouldn't happen either */ + if (!amount_msat_sub(shadow_fee, amount, f->amounts[numpath-1])) + plugin_err(pay_plugin->plugin, + "Failed to calc shadow fee: %s - %s", + type_to_string(tmpctx, struct amount_msat, &amount), + type_to_string(tmpctx, struct amount_msat, + &f->amounts[numpath-1])); + + return shadow_delay; +} + +static bool add_to_amounts(const struct gossmap *gossmap, + struct flow *f, + struct amount_msat maxspend, + struct amount_msat additional) +{ + struct amount_msat *amounts; + size_t num = tal_count(f->amounts); + + /* Recalculate amounts backwards */ + amounts = tal_arr(tmpctx, struct amount_msat, num); + if (!amount_msat_add(&amounts[num-1], f->amounts[num-1], additional)) + return false; + + for (int i = num-2; i >= 0; i--) { + amounts[i] = amounts[i+1]; + if (!amount_msat_add_fee(&amounts[i], + flow_edge(f, i)->base_fee, + flow_edge(f, i)->proportional_fee)) + return false; + } + + /* Do we now exceed budget? */ + if (amount_msat_greater(amounts[0], maxspend)) + return false; + + /* OK, replace amounts */ + tal_free(f->amounts); + f->amounts = tal_steal(f, amounts); + return true; +} + +static u64 flow_delay(const struct flow *flow) +{ + u64 delay = 0; + for (size_t i = 0; i < tal_count(flow->path); i++) + delay += flow->path[i]->half[flow->dirs[i]].delay; + return delay; +} + +/* This enhances f->amounts, and returns per-flow cltvs */ +static u32 *shadow_additions(const tal_t *ctx, + const struct gossmap *gossmap, + struct renepay *renepay, + struct flow **flows, + bool is_entire_payment) +{ + struct payment * p = renepay->payment; + u32 *final_cltvs; + + /* Set these up now in case we decide to do nothing */ + final_cltvs = tal_arr(ctx, u32, tal_count(flows)); + for (size_t i = 0; i < tal_count(flows); i++) + final_cltvs[i] = p->final_cltv; + + /* DEVELOPER can disable this */ + if (!p->use_shadow) + return final_cltvs; + + for (size_t i = 0; i < tal_count(flows); i++) { + u32 shadow_delay; + struct amount_msat shadow_fee; + + shadow_delay = shadow_one_flow(gossmap, flows[i], + &shadow_fee); + if (flow_delay(flows[i]) + shadow_delay > p->maxdelay) { + debug_paynote(p, "No shadow for flow %zu/%zu:" + " delay would add %u to %"PRIu64", exceeding max delay.", + i, tal_count(flows), + shadow_delay, + flow_delay(flows[i])); + continue; + } + + /* We don't need to add fee amounts to obfuscate most payments + * when we're using MPP, since we randomly split amounts. But + * if this really is the entire thing, we want to, since + * people use round numbers of msats in invoices. */ + if (is_entire_payment && tal_count(flows) == 1) { + if (!add_to_amounts(gossmap, flows[i], p->maxspend, + shadow_fee)) { + debug_paynote(p, "No shadow fee for flow %zu/%zu:" + " fee would add %s to %s, exceeding budget %s.", + i, tal_count(flows), + type_to_string(tmpctx, struct amount_msat, + &shadow_fee), + type_to_string(tmpctx, struct amount_msat, + &flows[i]->amounts[0]), + type_to_string(tmpctx, struct amount_msat, + &p->maxspend)); + } else { + debug_paynote(p, "No MPP, so added %s shadow fee", + type_to_string(tmpctx, struct amount_msat, + &shadow_fee)); + } + } + + final_cltvs[i] += shadow_delay; + debug_paynote(p, "Shadow route on flow %zu/%zu added %u block delay. now %u", + i, tal_count(flows), shadow_delay, final_cltvs[i]); + } + + return final_cltvs; +} + +/* Calculates delays and converts to scids. Frees flows. Caller is responsible + * for removing resultings flows from the chan_extra_map. */ +static struct pay_flow **flows_to_pay_flows(struct payment *payment, + struct gossmap *gossmap, + struct flow **flows STEALS, + const u32 *final_cltvs, + u64 *next_partid) +{ + struct pay_flow **pay_flows + = tal_arr(payment, struct pay_flow *, tal_count(flows)); + + for (size_t i = 0; i < tal_count(flows); i++) { + struct flow *f = flows[i]; + struct pay_flow *pf = tal(pay_flows, struct pay_flow); + size_t plen; + + plen = tal_count(f->path); + pay_flows[i] = pf; + + pf->payment = payment; + pf->key.partid = (*next_partid)++; + pf->key.groupid = payment->groupid; + pf->key.payment_hash = &payment->payment_hash; + + /* Convert gossmap_chan into scids and nodes */ + pf->path_scids = tal_arr(pf, struct short_channel_id, plen); + pf->path_nodes = tal_arr(pf, struct node_id, plen); + for (size_t j = 0; j < plen; j++) { + struct gossmap_node *n; + n = gossmap_nth_node(gossmap, f->path[j], !f->dirs[j]); + gossmap_node_get_id(gossmap, n, &pf->path_nodes[j]); + pf->path_scids[j] + = gossmap_chan_scid(gossmap, f->path[j]); + } + + /* Calculate cumulative delays (backwards) */ + pf->cltv_delays = tal_arr(pf, u32, plen); + pf->cltv_delays[plen-1] = final_cltvs[i]; + for (int j = (int)plen-2; j >= 0; j--) { + pf->cltv_delays[j] = pf->cltv_delays[j+1] + + f->path[j]->half[f->dirs[j]].delay; + } + pf->amounts = tal_steal(pf, f->amounts); + pf->path_dirs = tal_steal(pf, f->dirs); + pf->success_prob = f->success_prob; + pf->attempt = renepay_current_attempt(payment->renepay); + } + tal_free(flows); + + return pay_flows; +} + +static bitmap *make_disabled_bitmap(const tal_t *ctx, + const struct gossmap *gossmap, + const struct short_channel_id *scids) +{ + bitmap *disabled + = tal_arrz(ctx, bitmap, + BITMAP_NWORDS(gossmap_max_chan_idx(gossmap))); + + for (size_t i = 0; i < tal_count(scids); i++) { + struct gossmap_chan *c = gossmap_find_chan(gossmap, &scids[i]); + if (c) + bitmap_set_bit(disabled, gossmap_chan_idx(gossmap, c)); + } + return disabled; +} + + +static 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; +} + +/* FIXME: If only path has channels marked disabled, we should try... */ +static bool disable_htlc_violations_oneflow(struct renepay * renepay, + const struct flow *flow, + const struct gossmap *gossmap, + bitmap *disabled) +{ + struct payment * p = renepay->payment; + bool disabled_some = false; + + for (size_t i = 0; i < tal_count(flow->path); i++) { + const struct half_chan *h = &flow->path[i]->half[flow->dirs[i]]; + struct short_channel_id scid; + const char *reason; + + if (!h->enabled) + reason = "channel_update said it was disabled"; + else if (amount_msat_greater_fp16(flow->amounts[i], h->htlc_max)) + reason = "htlc above maximum"; + else if (amount_msat_less_fp16(flow->amounts[i], h->htlc_min)) + reason = "htlc below minimum"; + else + continue; + + scid = gossmap_chan_scid(gossmap, flow->path[i]); + debug_paynote(p, "...disabling channel %s: %s", + type_to_string(tmpctx, struct short_channel_id, &scid), + reason); + + /* Add this for future searches for this payment. */ + tal_arr_expand(&renepay->disabled, scid); + /* Add to existing bitmap */ + bitmap_set_bit(disabled, + gossmap_chan_idx(gossmap, flow->path[i])); + disabled_some = true; + } + return disabled_some; +} + +/* If we can't use one of these flows because we hit limits, we disable that + * channel for future searches and return false */ +static bool disable_htlc_violations(struct renepay *renepay, + struct flow **flows, + const struct gossmap *gossmap, + bitmap *disabled) +{ + bool disabled_some = false; + + /* We continue through all of them, to disable many at once. */ + for (size_t i = 0; i < tal_count(flows); i++) { + disabled_some |= disable_htlc_violations_oneflow(renepay, flows[i], + gossmap, + disabled); + } + return disabled_some; +} + +/* Get some payment flows to get this amount to destination, or NULL. */ +struct pay_flow **get_payflows(struct renepay * renepay, + struct amount_msat amount, + struct amount_msat feebudget, + bool unlikely_ok, + bool is_entire_payment, + char const ** err_msg) +{ + *err_msg = tal_fmt(tmpctx,"[no error]"); + + struct payment * p = renepay->payment; + bitmap *disabled; + struct pay_flow **pay_flows; + const struct gossmap_node *src, *dst; + + disabled = make_disabled_bitmap(tmpctx, pay_plugin->gossmap, renepay->disabled); + src = gossmap_find_node(pay_plugin->gossmap, &pay_plugin->my_id); + if (!src) { + debug_paynote(p, "We don't have any channels?"); + *err_msg = tal_fmt(tmpctx,"We don't have any channels."); + goto fail; + } + dst = gossmap_find_node(pay_plugin->gossmap, &p->destination); + if (!dst) { + debug_paynote(p, "No trace of destination in network gossip"); + *err_msg = tal_fmt(tmpctx,"Destination is unreacheable in the network gossip."); + goto fail; + } + + for (;;) { + struct flow **flows; + double prob; + struct amount_msat fee; + u64 delay; + bool too_unlikely, too_expensive, too_delayed; + const u32 *final_cltvs; + + flows = minflow(tmpctx, pay_plugin->gossmap, src, dst, + pay_plugin->chan_extra_map, disabled, + amount, + feebudget, + p->min_prob_success , + p->delay_feefactor, + p->base_fee_penalty, + p->prob_cost_factor); + if (!flows) { + debug_paynote(p, + "minflow couldn't find a feasible flow for %s", + type_to_string(tmpctx,struct amount_msat,&amount)); + + *err_msg = tal_fmt(tmpctx, + "minflow couldn't find a feasible flow for %s", + type_to_string(tmpctx,struct amount_msat,&amount)); + goto fail; + } + + /* Are we unhappy? */ + prob = flow_set_probability(flows,pay_plugin->gossmap,pay_plugin->chan_extra_map); + fee = flow_set_fee(flows); + delay = flows_worst_delay(flows) + p->final_cltv; + + too_unlikely = (prob < p->min_prob_success); + if (too_unlikely && !unlikely_ok) + { + debug_paynote(p, "Flows too unlikely, P() = %f%%", prob * 100); + *err_msg = tal_fmt(tmpctx, + "Probability is too small, " + "Prob = %f%% (min = %f%%)", + prob*100, + p->min_prob_success*100); + goto fail; + } + too_expensive = amount_msat_greater(fee, feebudget); + if (too_expensive) + { + debug_paynote(p, "Flows too expensive, fee = %s (max %s)", + type_to_string(tmpctx, struct amount_msat, &fee), + type_to_string(tmpctx, struct amount_msat, &feebudget)); + *err_msg = tal_fmt(tmpctx, + "Fee exceeds our fee budget, " + "fee = %s (maxfee = %s)", + type_to_string(tmpctx, struct amount_msat, &fee), + type_to_string(tmpctx, struct amount_msat, &feebudget)); + goto fail; + } + too_delayed = (delay > p->maxdelay); + if (too_delayed) { + debug_paynote(p, "Flows too delayed, delay = %"PRIu64" (max %u)", + delay, p->maxdelay); + + /* FIXME: What is a sane limit? */ + if (p->delay_feefactor > 1000) { + debug_paynote(p, "Giving up!"); + *err_msg = tal_fmt(tmpctx, + "CLTV delay exceeds our CLTV budget, " + "delay = %"PRIu64" (maxdelay = %u)", + delay,p->maxdelay); + goto fail; + } + + p->delay_feefactor *= 2; + debug_paynote(p, "Doubling delay_feefactor to %f", + p->delay_feefactor); + + continue; // retry + } + + /* Now we check for min/max htlc violations, and + * excessive htlc counts. It would be more efficient + * to do this inside minflow(), but the diagnostics here + * are far better, since we can report min/max which + * *actually* made us reconsider. */ + if (disable_htlc_violations(renepay, flows, pay_plugin->gossmap, + disabled)) + { + continue; // retry + } + + /* This can adjust amounts and final cltv for each flow, + * to make it look like it's going elsewhere */ + final_cltvs = shadow_additions(tmpctx, pay_plugin->gossmap, + renepay, flows, is_entire_payment); + /* OK, we are happy with these flows: convert to + * pay_flows to outlive the current gossmap. */ + pay_flows = flows_to_pay_flows(renepay->payment, pay_plugin->gossmap, + flows, final_cltvs, + &renepay->next_partid); + break; + } + + return pay_flows; + +fail: + return NULL; +} + +const char *flow_path_to_str(const tal_t *ctx, const struct pay_flow *flow) +{ + char *s = tal_strdup(ctx, ""); + for (size_t i = 0; i < tal_count(flow->path_scids); i++) { + tal_append_fmt(&s, "-%s->", + type_to_string(tmpctx, struct short_channel_id, + &flow->path_scids[i])); + } + return s; +} + +const char* fmt_payflows(const tal_t *ctx, + struct pay_flow ** flows) +{ + struct json_out *jout = json_out_new(ctx); + json_out_start(jout, NULL, '{'); + json_out_start(jout,"Pay_flows",'['); + + for(size_t i=0;isuccess_prob); + + json_out_start(jout,"path_scids",'['); + for(size_t j=0;jpath_scids);++j) + { + json_out_add(jout,NULL,true,"%s", + type_to_string(ctx,struct short_channel_id,&f->path_scids[j])); + } + json_out_end(jout,']'); + + json_out_start(jout,"path_dirs",'['); + for(size_t j=0;jpath_dirs);++j) + { + json_out_add(jout,NULL,false,"%d",f->path_dirs[j]); + } + json_out_end(jout,']'); + + json_out_start(jout,"amounts",'['); + for(size_t j=0;jamounts);++j) + { + json_out_add(jout,NULL,true,"%s", + type_to_string(ctx,struct amount_msat,&f->amounts[j])); + } + json_out_end(jout,']'); + + json_out_start(jout,"cltv_delays",'['); + for(size_t j=0;jcltv_delays);++j) + { + json_out_add(jout,NULL,false,"%d",f->cltv_delays[j]); + } + json_out_end(jout,']'); + + json_out_start(jout,"path_nodes",'['); + for(size_t j=0;jpath_nodes);++j) + { + json_out_add(jout,NULL,true,"%s", + type_to_string(ctx,struct node_id,&f->path_nodes[j])); + } + json_out_end(jout,']'); + + json_out_end(jout,'}'); + } + + json_out_end(jout,']'); + json_out_end(jout, '}'); + json_out_direct(jout, 1)[0] = '\n'; + json_out_direct(jout, 1)[0] = '\0'; + json_out_finished(jout); + + size_t len; + return json_out_contents(jout,&len); +} + +void remove_htlc_payflow( + struct chan_extra_map *chan_extra_map, + struct pay_flow *flow) +{ + for (size_t i = 0; i < tal_count(flow->path_scids); i++) { + struct chan_extra_half *h = get_chan_extra_half_by_scid( + chan_extra_map, + flow->path_scids[i], + flow->path_dirs[i]); + if(!h) + { + plugin_err(pay_plugin->plugin, + "%s could not resolve chan_extra_half", + __PRETTY_FUNCTION__); + } + if (!amount_msat_sub(&h->htlc_total, h->htlc_total, flow->amounts[i])) + { + plugin_err(pay_plugin->plugin, + "%s could not substract HTLC amounts, " + "half total htlc amount = %s, " + "flow->amounts[%lld] = %s.", + __PRETTY_FUNCTION__, + type_to_string(tmpctx, struct amount_msat, &h->htlc_total), + i, + type_to_string(tmpctx, struct amount_msat, &flow->amounts[i])); + } + if (h->num_htlcs == 0) + { + plugin_err(pay_plugin->plugin, + "%s could not decrease HTLC count.", + __PRETTY_FUNCTION__); + } + h->num_htlcs--; + } +} +void commit_htlc_payflow( + struct chan_extra_map *chan_extra_map, + const struct pay_flow *flow) +{ + for (size_t i = 0; i < tal_count(flow->path_scids); i++) { + struct chan_extra_half *h = get_chan_extra_half_by_scid( + chan_extra_map, + flow->path_scids[i], + flow->path_dirs[i]); + if(!h) + { + plugin_err(pay_plugin->plugin, + "%s could not resolve chan_extra_half", + __PRETTY_FUNCTION__); + } + if (!amount_msat_add(&h->htlc_total, h->htlc_total, flow->amounts[i])) + { + plugin_err(pay_plugin->plugin, + "%s could not add HTLC amounts, " + "flow->amounts[%lld] = %s.", + __PRETTY_FUNCTION__, + i, + type_to_string(tmpctx, struct amount_msat, &flow->amounts[i])); + } + h->num_htlcs++; + } +} + +/* How much does this flow deliver to destination? */ +struct amount_msat payflow_delivered(const struct pay_flow *flow) +{ + return flow->amounts[tal_count(flow->amounts)-1]; +} + +struct pay_flow* payflow_fail(struct pay_flow *flow) +{ + debug_assert(flow); + struct payment * p = flow->payment; + debug_assert(p); + + payment_fail(p); + amount_msat_reduce(&p->total_delivering, payflow_delivered(flow)); + amount_msat_reduce(&p->total_sent, flow->amounts[0]); + + /* Release the HTLCs in the uncertainty_network. */ + return tal_free(flow); +} + + diff --git a/plugins/renepay/pay_flow.h b/plugins/renepay/pay_flow.h new file mode 100644 index 000000000..26d37a69c --- /dev/null +++ b/plugins/renepay/pay_flow.h @@ -0,0 +1,112 @@ +#ifndef LIGHTNING_PLUGINS_RENEPAY_PAY_FLOW_H +#define LIGHTNING_PLUGINS_RENEPAY_PAY_FLOW_H +#include "config.h" +#include +#include +#include +#include +#include +#include + +/* This is like a struct flow, but independent of gossmap, and contains + * all we need to actually send the part payment. */ +struct pay_flow { + /* So we can be an independent object for callbacks. */ + struct payment * payment; + + // TODO(eduardo): remove this, unnecessary + int attempt; + + /* Information to link this flow to a unique sendpay. */ + struct payflow_key + { + // TODO(eduardo): pointer or value? + struct sha256 *payment_hash; + u64 groupid; + u64 partid; + } key; + + /* The series of channels and nodes to traverse. */ + struct short_channel_id *path_scids; + struct node_id *path_nodes; + int *path_dirs; + /* CLTV delays for each hop */ + u32 *cltv_delays; + /* The amounts at each step */ + struct amount_msat *amounts; + /* Probability estimate (0-1) */ + double success_prob; +}; + +static inline struct payflow_key +payflow_key(struct sha256 *hash, u64 groupid, u64 partid) +{ + struct payflow_key k= {hash,groupid,partid}; + return k; +} + +static inline const char* fmt_payflow_key( + const tal_t *ctx, + const struct payflow_key * k) +{ + char *str = tal_fmt( + ctx, + "key: groupid=%ld, partid=%ld, payment_hash=%s", + k->groupid,k->partid, + type_to_string(ctx,struct sha256,k->payment_hash)); + return str; +} + + +static inline const struct payflow_key +payflow_get_key(const struct pay_flow * pf) +{ + return pf->key; +} + +static inline size_t payflow_key_hash(const struct payflow_key k) +{ + return k.payment_hash->u.u32[0] ^ (k.groupid << 32) ^ k.partid; +} + +static inline bool payflow_key_equal(struct pay_flow const *pf, + const struct payflow_key k) +{ + return pf->key.partid==k.partid && pf->key.groupid==k.groupid + && sha256_eq(pf->key.payment_hash,k.payment_hash); +} + +HTABLE_DEFINE_TYPE(struct pay_flow, + payflow_get_key, payflow_key_hash, payflow_key_equal, + payflow_map); + + +struct pay_flow **get_payflows(struct renepay * renepay, + struct amount_msat amount, + struct amount_msat feebudget, + bool unlikely_ok, + bool is_entire_payment, + char const ** err_msg); + +void commit_htlc_payflow( + struct chan_extra_map *chan_extra_map, + const struct pay_flow *flow); + +void remove_htlc_payflow( + struct chan_extra_map *chan_extra_map, + struct pay_flow *flow); + +const char *flow_path_to_str(const tal_t *ctx, const struct pay_flow *flow); + +const char* fmt_payflows(const tal_t *ctx, + struct pay_flow ** flows); + +/* How much does this flow deliver to destination? */ +struct amount_msat payflow_delivered(const struct pay_flow *flow); + +/* Removes amounts from payment and frees flow pointer. + * A possible destructor for flow would remove HTLCs from the + * uncertainty_network and remove the flow from any data structure. */ +struct pay_flow* payflow_fail(struct pay_flow *flow); + +#endif /* LIGHTNING_PLUGINS_RENEPAY_PAY_FLOW_H */ diff --git a/plugins/renepay/payment.c b/plugins/renepay/payment.c new file mode 100644 index 000000000..17d1f9f01 --- /dev/null +++ b/plugins/renepay/payment.c @@ -0,0 +1,230 @@ +#include "config.h" +#include +#include +#include + +struct payment * payment_new(struct renepay * renepay) +{ + struct payment *p = tal(renepay,struct payment); + p->renepay = renepay; + p->paynotes = tal_arr(p, const char *, 0); + + p->total_sent = AMOUNT_MSAT(0); + p->total_delivering = AMOUNT_MSAT(0); + + p->invstr=NULL; + + p->amount = AMOUNT_MSAT(0); + // p->destination= + // p->payment_hash + p->maxspend = AMOUNT_MSAT(0); + p->maxdelay=0; + // p->start_time= + // p->stop_time= + p->preimage = NULL; + p->payment_secret=NULL; + p->payment_metadata=NULL; + p->status=PAYMENT_PENDING; + p->final_cltv=0; + // p->list= + p->description=NULL; + p->label=NULL; + + p->delay_feefactor=0; + p->base_fee_penalty=0; + p->prob_cost_factor=0; + p->min_prob_success=0; + + p->local_offer_id=NULL; + p->use_shadow=true; + p->groupid=1; + + p->result = NULL; + return p; +} + +struct renepay * renepay_new(struct command *cmd) +{ + struct renepay *renepay = tal(cmd,struct renepay); + + renepay->cmd = cmd; + renepay->payment = payment_new(renepay); + renepay->localmods_applied=false; + renepay->local_gossmods = gossmap_localmods_new(renepay); + renepay->disabled = tal_arr(renepay,struct short_channel_id,0); + renepay->rexmit_timer = NULL; + renepay->next_attempt=1; + renepay->next_partid=1; + renepay->all_flows = tal(renepay,tal_t); + + return renepay; +} + + +void payment_fail(struct payment * p) +{ + /* If the payment already succeeded this function call must correspond + * to an old sendpay. */ + if(p->status == PAYMENT_SUCCESS)return; + p->status=PAYMENT_FAIL; +} +void payment_success(struct payment * p) +{ + p->status=PAYMENT_SUCCESS; +} + +struct amount_msat payment_sent(struct payment const * p) +{ + return p->total_sent; +} +struct amount_msat payment_delivered(struct payment const * p) +{ + return p->total_delivering; +} +struct amount_msat payment_amount(struct payment const * p) +{ + return p->amount; +} + +struct amount_msat payment_fees(struct payment const*p) +{ + struct amount_msat fees; + struct amount_msat sent = payment_sent(p), + delivered = payment_delivered(p); + + if(!amount_msat_sub(&fees,sent,delivered)) + debug_err( "Strange, sent amount (%s) is less than delivered (%s), aborting.", + type_to_string(tmpctx,struct amount_msat,&sent), + type_to_string(tmpctx,struct amount_msat,&delivered)); + return fees; +} + +void payment_note(struct payment *p, const char *fmt, ...) +{ + va_list ap; + const char *str; + + va_start(ap, fmt); + str = tal_vfmt(p->paynotes, fmt, ap); + va_end(ap); + tal_arr_expand(&p->paynotes, str); + debug_info("%s",str); +} + +void payment_assert_delivering_incomplete(struct payment const * p) +{ + if(!amount_msat_less(p->total_delivering, p->amount)) + { + debug_err( + "Strange, delivering (%s) is not smaller than amount (%s)", + type_to_string(tmpctx,struct amount_msat,&p->total_delivering), + type_to_string(tmpctx,struct amount_msat,&p->amount)); + } +} +void payment_assert_delivering_all(struct payment const * p) +{ + if(amount_msat_less(p->total_delivering, p->amount)) + { + debug_err( + "Strange, delivering (%s) is less than amount (%s)", + type_to_string(tmpctx,struct amount_msat,&p->total_delivering), + type_to_string(tmpctx,struct amount_msat,&p->amount)); + } +} + + +int renepay_current_attempt(const struct renepay * renepay) +{ + return renepay->next_attempt-1; +} +int renepay_attempt_count(const struct renepay * renepay) +{ + return renepay->next_attempt-1; +} +void renepay_new_attempt(struct renepay * renepay) +{ + renepay->payment->status=PAYMENT_PENDING; + renepay->next_attempt++; +} +struct command_result *renepay_success(struct renepay * renepay) +{ + debug_info("calling %s",__PRETTY_FUNCTION__); + struct payment *p = renepay->payment; + + payment_success(p); + payment_assert_delivering_all(p); + + struct json_stream *response + = jsonrpc_stream_success(renepay->cmd); + + /* Any one succeeding is success. */ + json_add_preimage(response, "payment_preimage", p->preimage); + json_add_sha256(response, "payment_hash", &p->payment_hash); + json_add_timeabs(response, "created_at", p->start_time); + json_add_u32(response, "parts", renepay_parts(renepay)); + json_add_amount_msat(response, "amount_msat", + p->amount); + json_add_amount_msat(response, "amount_sent_msat", + p->total_sent); + json_add_string(response, "status", "complete"); + json_add_node_id(response, "destination", &p->destination); + + return command_finished(renepay->cmd, response); +} + +struct command_result *renepay_fail( + struct renepay * renepay, + enum jsonrpc_errcode code, + const char *fmt, ...) +{ + /* renepay_fail is called after command finished. */ + if(renepay==NULL) + { + return command_still_pending(NULL); + } + payment_fail(renepay->payment); + + va_list args; + va_start(args, fmt); + char *message = tal_vfmt(tmpctx,fmt,args); + va_end(args); + + debug_paynote(renepay->payment,"%s",message); + + return command_fail(renepay->cmd,code,"%s",message); +} + +u64 renepay_parts(struct renepay const * renepay) +{ + return renepay->next_partid-1; +} + +/* Either the payment succeeded or failed, we need to cleanup/set the plugin + * into a valid state before the next payment. */ +void renepay_cleanup( + struct renepay * renepay, + struct gossmap * gossmap) +{ + debug_info("calling %s",__PRETTY_FUNCTION__); + /* Always remove our local mods (routehints) so others can use + * gossmap. We do this only after the payment completes. */ + // TODO(eduardo): it can happen that local_gossmods removed below + // contained a set of channels for which there is information in the + // uncertainty network (chan_extra_map) and that are part of some pending + // payflow (payflow_map). Handle this situation. + if(renepay->localmods_applied) + gossmap_remove_localmods(gossmap, + renepay->local_gossmods); + // TODO(eduardo): I wonder if it is possible to have two instances of + // renepay at the same time. + // 1st problem: dijkstra datastructure is global, this can be fixed, + // 2nd problem: we don't know if gossmap_apply_localmods and gossmap_remove_localmods, + // can handle different local_gossmods applied to the same gossmap. + renepay->localmods_applied=false; + tal_free(renepay->local_gossmods); + + renepay->rexmit_timer = tal_free(renepay->rexmit_timer); + + if(renepay->payment) + renepay->payment->renepay = NULL; +} diff --git a/plugins/renepay/payment.h b/plugins/renepay/payment.h new file mode 100644 index 000000000..f1b947f18 --- /dev/null +++ b/plugins/renepay/payment.h @@ -0,0 +1,163 @@ +#ifndef LIGHTNING_PLUGINS_RENEPAY_PAYMENT_H +#define LIGHTNING_PLUGINS_RENEPAY_PAYMENT_H +#include "config.h" +#include +#include + +enum payment_status { + PAYMENT_PENDING, PAYMENT_SUCCESS, PAYMENT_FAIL +}; + + +struct payment { + struct renepay * renepay; + + /* Chatty description of attempts. */ + const char **paynotes; + + /* Total sent, including fees. */ + struct amount_msat total_sent; + + /* Total that is delivering (i.e. without fees) */ + struct amount_msat total_delivering; + + /* invstring (bolt11 or bolt12) */ + const char *invstr; + + /* How much, what, where */ + struct amount_msat amount; + struct node_id destination; + struct sha256 payment_hash; + + + /* Limits on what routes we'll accept. */ + struct amount_msat maxspend; + + /* Max accepted HTLC delay.*/ + unsigned int maxdelay; + + /* We promised this in pay() output */ + struct timeabs start_time; + + /* We stop trying after this time is reached. */ + struct timeabs stop_time; + + /* Payment preimage, in case of success. */ + const struct preimage *preimage; + + /* payment_secret, if specified by invoice. */ + struct secret *payment_secret; + + /* Payment metadata, if specified by invoice. */ + const u8 *payment_metadata; + + /* To know if the last attempt failed, succeeded or is it pending. */ + enum payment_status status; + + u32 final_cltv; + + /* Inside pay_plugin->payments list */ + struct list_node list; + + /* Description and labels, if any. */ + const char *description, *label; + + + /* Penalty for CLTV delays */ + double delay_feefactor; + + /* Penalty for base fee */ + double base_fee_penalty; + + /* With these the effective linear fee cost is computed as + * + * linear fee cost = + * millionths + * + base_fee* base_fee_penalty + * +delay*delay_feefactor; + * */ + + /* The minimum acceptable prob. of success */ + double min_prob_success; + + /* Conversion from prob. cost to millionths */ + double prob_cost_factor; + /* linear prob. cost = + * - prob_cost_factor * log prob. */ + + + /* If this is paying a local offer, this is the one (sendpay ensures we + * don't pay twice for single-use offers) */ + // TODO(eduardo): this is not being used! + struct sha256 *local_offer_id; + + /* DEVELOPER allows disabling shadow route */ + bool use_shadow; + + /* Groupid, so listpays() can group them back together */ + u64 groupid; + + struct command_result * result; +}; + +/* Data only kept while the payment is being processed. */ +struct renepay +{ + /* The command, and our owner (needed for timer func) */ + struct command *cmd; + + /* Payment information that will eventually outlive renepay and be + * registered. */ + struct payment * payment; + + /* Localmods to apply to gossip_map for our own use. */ + bool localmods_applied; + struct gossmap_localmods *local_gossmods; + + /* Channels we decided to disable for various reasons. */ + struct short_channel_id *disabled; + + /* Timers. */ + struct plugin_timer *rexmit_timer; + + /* Keep track of the number of attempts. */ + int next_attempt; + /* Used in get_payflows to set ids to each pay_flow. */ + u64 next_partid; + + /* Root to destroy pending flows */ + tal_t *all_flows; +}; + +struct payment * payment_new(struct renepay *renepay); +struct renepay * renepay_new(struct command *cmd); +void renepay_cleanup( + struct renepay * renepay, + struct gossmap * gossmap); + +void payment_fail(struct payment * p); +void payment_success(struct payment * p); +struct amount_msat payment_sent(struct payment const * p); +struct amount_msat payment_delivered(struct payment const * p); +struct amount_msat payment_amount(struct payment const * p); +struct amount_msat payment_fees(struct payment const*p); + +void payment_note(struct payment *p, const char *fmt, ...); +void payment_assert_delivering_incomplete(struct payment const * p); +void payment_assert_delivering_all(struct payment const * p); + + +int renepay_current_attempt(const struct renepay *renepay); +int renepay_attempt_count(const struct renepay *renepay); +void renepay_new_attempt(struct renepay *renepay); + +struct command_result *renepay_success(struct renepay *renepay); + +struct command_result *renepay_fail( + struct renepay * renepay, + enum jsonrpc_errcode code, + const char *fmt, ...); + +u64 renepay_parts(struct renepay const * renepay); + +#endif /* LIGHTNING_PLUGINS_RENEPAY_PAYMENT_H */ diff --git a/plugins/renepay/test/Makefile b/plugins/renepay/test/Makefile new file mode 100644 index 000000000..b00740a74 --- /dev/null +++ b/plugins/renepay/test/Makefile @@ -0,0 +1,19 @@ +# Note that these actually #include everything they need, except ccan/ and bitcoin/. +# That allows for unit testing of statics, and special effects. +PLUGIN_RENEPAY_TEST_SRC := $(wildcard plugins/renepay/test/run-*.c) +PLUGIN_RENEPAY_TEST_OBJS := $(PLUGIN_RENEPAY_TEST_SRC:.c=.o) +PLUGIN_RENEPAY_TEST_PROGRAMS := $(PLUGIN_RENEPAY_TEST_OBJS:.o=) + +ALL_C_SOURCES += $(PLUGIN_RENEPAY_TEST_SRC) +ALL_TEST_PROGRAMS += $(PLUGIN_RENEPAY_TEST_PROGRAMS) +$(PLUGIN_RENEPAY_TEST_OBJS): $(PLUGIN_RENEPAY_SRC) + +PLUGIN_RENEPAY_TEST_COMMON_OBJS := \ + plugins/renepay/dijkstra.o \ + plugins/renepay/debug.o + +$(PLUGIN_RENEPAY_TEST_PROGRAMS): $(PLUGIN_RENEPAY_TEST_COMMON_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) bitcoin/chainparams.o common/gossmap.o common/fp16.o common/dijkstra.o common/bolt12.o common/bolt12_merkle.o wire/bolt12$(EXP)_wiregen.o + +check-renepay: $(PLUGIN_RENEPAY_TEST_PROGRAMS:%=unittest/%) + +check-units: check-renepay diff --git a/plugins/renepay/test/run-dijkstra.c b/plugins/renepay/test/run-dijkstra.c new file mode 100644 index 000000000..e5eab3ea0 --- /dev/null +++ b/plugins/renepay/test/run-dijkstra.c @@ -0,0 +1,100 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include + +#include + +static void insertion_in_increasing_distance(const tal_t *ctx) +{ + dijkstra_malloc(ctx,10); + + for(int i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 = flow_set_probability(flows,gossmap,chan_extra_map); + 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 ? "->" : "", + type_to_string(this_ctx, struct short_channel_id, &scid)); + } + delivered = flows[i]->amounts[tal_count(flows[i]->amounts)-1]; + if (!amount_msat_sub(&fee, flows[i]->amounts[0], delivered)) + { + debug_err("%s: flow[i]->amount[0]success_prob, + type_to_string(this_ctx, struct amount_msat, &delivered), + type_to_string(this_ctx, struct amount_msat, &fee)); + } + + tal_free(this_ctx); + return buff; +} + +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; + + common_setup(argv[0]); + + fd = tmpdir_mkstemp(tmpctx, "run-not_mcf-diamond.XXXXXX", &gossfile); + assert(write_all(fd, empty_map, sizeof(empty_map))); + + gossmap = gossmap_load(tmpctx, gossfile, 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, NULL)); + assert(gossmap_local_updatechan(mods, &scid12, + /*htlc_min=*/ AMOUNT_MSAT(0), + /*htlc_max=*/ AMOUNT_MSAT(10000000), + /*base_fee=*/ 0, + /*ppm_fee =*/ 1001, + /* delay =*/ 5, + /* enabled=*/ true, + /* dir =*/ 0)); + assert(gossmap_local_addchan(mods, &l2, &l4, &scid24, NULL)); + assert(gossmap_local_updatechan(mods, &scid24, + AMOUNT_MSAT(0), + AMOUNT_MSAT(10000000), + 0, 1002, 5, + true, + 0)); + assert(gossmap_local_addchan(mods, &l1, &l3, &scid13, NULL)); + assert(gossmap_local_updatechan(mods, &scid13, + AMOUNT_MSAT(0), + AMOUNT_MSAT(5000000), + 0, 503, 5, + true, + 0)); + assert(gossmap_local_addchan(mods, &l3, &l4, &scid34, NULL)); + assert(gossmap_local_updatechan(mods, &scid34, + AMOUNT_MSAT(0), + AMOUNT_MSAT(5000000), + 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)); + + struct flow **flows; + flows = minflow(tmpctx, gossmap, + gossmap_find_node(gossmap, &l1), + gossmap_find_node(gossmap, &l4), + chan_extra_map, NULL, + /* Half the capacity */ + AMOUNT_MSAT(1000000), // 1000 sats + /* max_fee = */ AMOUNT_MSAT(10000), // 10 sats + /* min probability = */ 0.8, // 80% + /* delay fee factor = */ 0, + /* base fee penalty */ 0, + /* prob cost factor = */ 1); + + debug_info("%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 new file mode 100644 index 000000000..d0a2dda2d --- /dev/null +++ b/plugins/renepay/test/run-mcf.c @@ -0,0 +1,470 @@ +#include "config.h" + +#define RENEPAY_UNITTEST // logs are written in /tmp/debug.txt +#include "../payment.c" +#include "../flow.c" +#include "../uncertainty_network.c" +#include "../mcf.c" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Canned gossmap, taken from tests/test_gossip.py's + * setup_gossip_store_test via od -v -Anone -tx1 < /tmp/ltests-kaf30pn0/test_gossip_store_compact_noappend_1/lightning-2/regtest/gossip_store + */ +static u8 canned_map[] = { + 0x09, 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, 0x80, 0x00, 0x00, 0x8e, 0x33, 0x3b, 0x90 + , 0x12, 0x00, 0x00, 0x00, 0x00, 0x10, 0x06, 0x00, 0x8a, 0x01, 0x02, 0x14, 0xb8, 0x21, 0x42, 0x7d + , 0x40, 0x89, 0x60, 0x71, 0x05, 0x8d, 0xe4, 0x50, 0x8e, 0xc3, 0x87, 0x6f, 0xa6, 0x4b, 0x19, 0xe4 + , 0x81, 0xc5, 0x5f, 0xb7, 0x04, 0xb8, 0x74, 0x08, 0x0b, 0x40, 0x5a, 0x74, 0x89, 0xbc, 0x63, 0x24 + , 0x27, 0x93, 0x4d, 0xfc, 0x1a, 0x72, 0xe4, 0xc7, 0xf8, 0x9b, 0xc1, 0x6b, 0xad, 0x9b, 0x04, 0x2e + , 0x14, 0xa4, 0xe9, 0xf5, 0x80, 0xf1, 0x02, 0x8f, 0x50, 0xf3, 0x2c, 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, 0x60, 0x17, 0x53, 0x70, 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, 0x3e, 0xa2, 0x81, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x10 + , 0x06, 0x00, 0x8a, 0x01, 0x02, 0x01, 0x0a, 0xb3, 0x54, 0x3f, 0xd2, 0xa9, 0xf5, 0x30, 0x0f, 0x60 + , 0x7d, 0xf9, 0xf1, 0xdd, 0x63, 0x62, 0xd8, 0xde, 0xe2, 0x94, 0xe4, 0x68, 0xc9, 0x5c, 0xe8, 0x32 + , 0x9b, 0x14, 0xd9, 0xf8, 0x6a, 0x23, 0x3a, 0x67, 0x10, 0x09, 0x64, 0x96, 0x40, 0xcb, 0x0b, 0xf5 + , 0xec, 0xe6, 0xba, 0x8e, 0x77, 0xb4, 0x6a, 0xf1, 0x39, 0x94, 0x86, 0xb0, 0x69, 0xd5, 0x17, 0x67 + , 0x83, 0xda, 0xfa, 0x49, 0x63, 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, 0x60, 0x17, 0x53 + , 0x70, 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 + , 0x0a, 0x01, 0xf0, 0xcb, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x10, 0x07, 0x00, 0x00, 0x67, 0x00, 0x00 + , 0x01, 0x00, 0x00, 0x40, 0x00, 0x01, 0xb0, 0xd2, 0xfa, 0x8f, 0x8d, 0x60, 0x17, 0x53, 0x70, 0x01 + , 0x00, 0x24, 0xfd, 0xae, 0x1a, 0xc8, 0x40, 0xa7, 0x33, 0x22, 0xe1, 0x45, 0x7e, 0x76, 0xb8, 0x86 + , 0xdd, 0x17, 0x8c, 0xd4, 0x49, 0x4b, 0x14, 0x3f, 0x81, 0xd4, 0xd4, 0xfa, 0xa7, 0x16, 0x17, 0xd2 + , 0x51, 0x33, 0x9e, 0xcb, 0x0e, 0x22, 0x1c, 0xf6, 0x02, 0x3a, 0x2e, 0x3e, 0x94, 0xf8, 0xae, 0xdb + , 0xee, 0x47, 0x23, 0xda, 0x5c, 0x35, 0x51, 0x57, 0xd8, 0xe4, 0x67, 0x2b, 0x46, 0x82, 0x5e, 0xc7 + , 0x98, 0x51, 0xb3, 0xb0, 0x1a, 0x2c, 0x72, 0x3f, 0x9b, 0xf5, 0xdb, 0xa8, 0xe3, 0x5f, 0x8b, 0x47 + , 0x9d, 0x9c, 0xd9, 0x73, 0xae, 0xc5, 0x0c, 0xca, 0x08, 0xfb, 0x97, 0x57, 0xb5, 0x21, 0x92, 0x05 + , 0x18, 0x42, 0x2d, 0x68, 0x19, 0x70, 0x76, 0x30, 0x61, 0x24, 0xff, 0xa5, 0xb6, 0x58, 0xa2, 0xe2 + , 0xb3, 0x68, 0x93, 0x37, 0xda, 0x6c, 0x3c, 0xcc, 0x5e, 0xf7, 0x3b, 0x51, 0x29, 0x64, 0x30, 0xbe + , 0x2a, 0x19, 0x38, 0x88, 0x9d, 0xda, 0x2a, 0xd1, 0xcb, 0x5e, 0x33, 0xdb, 0x75, 0xcf, 0x2e, 0x0e + , 0xfd, 0xbd, 0x38, 0xce, 0x01, 0x54, 0x62, 0x30, 0xb4, 0xdd, 0xdc, 0x7f, 0x67, 0xca, 0xf8, 0x39 + , 0x10, 0x02, 0x8a, 0x05, 0x3b, 0x76, 0x62, 0x72, 0xd2, 0x84, 0x71, 0x19, 0x19, 0x30, 0x92, 0xfa + , 0x2a, 0x1f, 0xdf, 0x71, 0xe3, 0xd8, 0x4a, 0x56, 0xd0, 0xe4, 0x35, 0xfe, 0x5d, 0x4a, 0x5b, 0x5b + , 0x90, 0x05, 0x28, 0xe4, 0x3b, 0x24, 0x13, 0x46, 0x99, 0x45, 0xc4, 0x92, 0x14, 0x7d, 0x43, 0x21 + , 0x06, 0x50, 0x51, 0xf8, 0x5b, 0x92, 0xb5, 0xb0, 0x90, 0xb1, 0xd7, 0x0d, 0x5a, 0xac, 0xfe, 0xf4 + , 0xe2, 0x70, 0x3e, 0x97, 0x42, 0x25, 0xfb, 0x21, 0x15, 0xf6, 0xb9, 0x32, 0xc8, 0xc3, 0x03, 0xbd + , 0x7a, 0xbd, 0x86, 0xf7, 0xcd, 0x64, 0xe6, 0x1a, 0x7f, 0x5a, 0x04, 0x7a, 0x22, 0xad, 0x7c, 0xfc + , 0x6a, 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, 0xd5, 0x95 + , 0xae, 0x92, 0xb3, 0x54, 0x4c, 0x32, 0x50, 0xfb, 0x77, 0x2f, 0x21, 0x4a, 0xd8, 0xd4, 0xc5, 0x14 + , 0x25, 0x03, 0x37, 0x40, 0xa5, 0xbc, 0xc3, 0x57, 0x19, 0x0a, 0xdd, 0x6d, 0x7e, 0x7a, 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, 0xc0, 0x00, 0x00, 0x8a, 0xf3, 0x48, 0xd5, 0xb3, 0x60, 0x17, 0x53 + , 0x70, 0x01, 0x02, 0x14, 0xb8, 0x21, 0x42, 0x7d, 0x40, 0x89, 0x60, 0x71, 0x05, 0x8d, 0xe4, 0x50 + , 0x8e, 0xc3, 0x87, 0x6f, 0xa6, 0x4b, 0x19, 0xe4, 0x81, 0xc5, 0x5f, 0xb7, 0x04, 0xb8, 0x74, 0x08 + , 0x0b, 0x40, 0x5a, 0x74, 0x89, 0xbc, 0x63, 0x24, 0x27, 0x93, 0x4d, 0xfc, 0x1a, 0x72, 0xe4, 0xc7 + , 0xf8, 0x9b, 0xc1, 0x6b, 0xad, 0x9b, 0x04, 0x2e, 0x14, 0xa4, 0xe9, 0xf5, 0x80, 0xf1, 0x02, 0x8f + , 0x50, 0xf3, 0x2c, 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, 0x60, 0x17, 0x53, 0x70, 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, 0xc0, 0x00, 0x00, 0x8a, 0xfe + , 0xd1, 0xc4, 0x47, 0x60, 0x17, 0x53, 0x70, 0x01, 0x02, 0x01, 0x0a, 0xb3, 0x54, 0x3f, 0xd2, 0xa9 + , 0xf5, 0x30, 0x0f, 0x60, 0x7d, 0xf9, 0xf1, 0xdd, 0x63, 0x62, 0xd8, 0xde, 0xe2, 0x94, 0xe4, 0x68 + , 0xc9, 0x5c, 0xe8, 0x32, 0x9b, 0x14, 0xd9, 0xf8, 0x6a, 0x23, 0x3a, 0x67, 0x10, 0x09, 0x64, 0x96 + , 0x40, 0xcb, 0x0b, 0xf5, 0xec, 0xe6, 0xba, 0x8e, 0x77, 0xb4, 0x6a, 0xf1, 0x39, 0x94, 0x86, 0xb0 + , 0x69, 0xd5, 0x17, 0x67, 0x83, 0xda, 0xfa, 0x49, 0x63, 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, 0x60, 0x17, 0x53, 0x70, 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, 0x40, 0x00, 0x00, 0x9b, 0x4f, 0x9d, 0xb7, 0xb9, 0x60, 0x17, 0x53, 0x77, 0x01, 0x01, 0x6e + , 0x99, 0xf5, 0x9c, 0x1f, 0x21, 0x8d, 0x4a, 0x2b, 0x6e, 0x36, 0x9a, 0x95, 0x20, 0x76, 0x2c, 0x27 + , 0xfb, 0xa8, 0xb1, 0x82, 0x1f, 0x64, 0x34, 0x93, 0x91, 0x9c, 0xeb, 0xfa, 0x40, 0x50, 0x73, 0x4d + , 0x00, 0xce, 0x10, 0xbf, 0x3f, 0x42, 0x3e, 0x56, 0x8f, 0xf8, 0xe0, 0x59, 0x58, 0xb5, 0xbd, 0xc5 + , 0x00, 0x82, 0xe3, 0x27, 0x92, 0x5b, 0xf8, 0x4f, 0x2c, 0x39, 0xec, 0x49, 0x3b, 0x07, 0x5e, 0x00 + , 0x0d, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x22, 0xaa, 0xa2, 0x60, 0x17 + , 0x53, 0x77, 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, 0x2d, 0x35, 0x36, 0x2d, 0x67, 0x64, 0x64, 0x31, 0x35, 0x33, 0x63, 0x38, 0x2d + , 0x6d, 0x6f, 0x64, 0x64, 0x65, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x15, 0x33, 0x0c, 0x6b + , 0x60, 0x17, 0x53, 0x77, 0x01, 0x01, 0x0e, 0x07, 0xaf, 0xd2, 0x33, 0x19, 0x0e, 0x06, 0x01, 0x6d + , 0x57, 0x88, 0x4e, 0x66, 0xf8, 0x08, 0xd9, 0x65, 0x8a, 0x73, 0xfb, 0x1d, 0xe0, 0xad, 0xee, 0x47 + , 0xf8, 0x1c, 0xfc, 0xc3, 0xd2, 0xfd, 0x06, 0x3e, 0x5a, 0x05, 0x65, 0x72, 0x18, 0x61, 0xb8, 0x23 + , 0x04, 0x3d, 0x4b, 0x39, 0x79, 0xe0, 0x85, 0x38, 0xd2, 0x92, 0x14, 0x35, 0x32, 0xaa, 0x9f, 0xab + , 0x5f, 0x98, 0x2c, 0x53, 0xfe, 0x0d, 0x00, 0x0d, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00 + , 0x00, 0x00, 0x22, 0xaa, 0xa2, 0x60, 0x17, 0x53, 0x77, 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, 0x33, 0x2d, 0x35, 0x36, 0x2d, 0x67, 0x64 + , 0x64, 0x31, 0x35, 0x33, 0x63, 0x38, 0x2d, 0x6d, 0x6f, 0x64, 0x64, 0x65, 0x64, 0x00, 0x00, 0x40 + , 0x00, 0x00, 0x8a, 0x22, 0x55, 0xdf, 0xb2, 0x60, 0x17, 0x53, 0x79, 0x01, 0x02, 0x3a, 0x2b, 0xe5 + , 0x81, 0x83, 0xa3, 0x1a, 0x49, 0x93, 0x89, 0x8d, 0xac, 0xa7, 0xb2, 0x2e, 0xc3, 0x94, 0x6c, 0xd1 + , 0xd6, 0xd0, 0x82, 0x34, 0xf3, 0x9c, 0x71, 0xa0, 0xd1, 0xdb, 0x3f, 0xcc, 0xfc, 0x53, 0xce, 0x8c + , 0x84, 0x3d, 0x14, 0x2c, 0x81, 0x4a, 0x07, 0xf0, 0x00, 0x03, 0x7a, 0x28, 0x10, 0xf4, 0xb9, 0x50 + , 0xb3, 0x22, 0x00, 0xdf, 0xc2, 0xc7, 0xfb, 0x6f, 0xf3, 0xfb, 0xf6, 0x94, 0x8e, 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, 0x60, 0x17, 0x53, 0x79, 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, 0xbc, 0x4d, 0x34, 0xb9, 0xcd, 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, 0x01, 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, 0xf4, 0xbc, 0x2c, 0x78, 0x00, 0x00, 0x00, 0x00, 0x10, 0x06, 0x00, 0x8a, 0x01, 0x02, 0x7a + , 0x2a, 0x3b, 0xad, 0x69, 0xf3, 0x8b, 0xba, 0xd2, 0xd3, 0xa2, 0x99, 0x66, 0x5f, 0x2d, 0x14, 0xc2 + , 0xca, 0xc2, 0xf4, 0x84, 0x97, 0x21, 0x93, 0x2f, 0xfd, 0x44, 0x19, 0xf6, 0xfa, 0x7f, 0x21, 0x3c + , 0x61, 0x45, 0x1e, 0x67, 0xfd, 0x5f, 0x9e, 0xee, 0x35, 0x03, 0xda, 0x96, 0xc3, 0x37, 0x2b, 0xfd + , 0x99, 0xb4, 0xdb, 0x0b, 0x6e, 0xa3, 0xdc, 0x8e, 0xad, 0x64, 0xf5, 0x9a, 0x4f, 0x5f, 0xae, 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, 0x01, 0x60, 0x17, 0x53, 0x7a, 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, 0xc3, 0xd8, 0xfd, 0x83, 0x00 + , 0x00, 0x00, 0x00, 0x10, 0x06, 0x00, 0x8a, 0x01, 0x02, 0x17, 0xdf, 0xc0, 0xb6, 0x5f, 0x8f, 0x42 + , 0x50, 0xe1, 0x4d, 0x35, 0xe7, 0x57, 0x2a, 0x07, 0x66, 0x8e, 0xa9, 0xe2, 0x61, 0xbf, 0xbc, 0x91 + , 0x5c, 0xa1, 0x80, 0x43, 0xcf, 0xb2, 0xba, 0x40, 0xf6, 0x2f, 0x0d, 0x37, 0x2c, 0xbc, 0x90, 0x96 + , 0x71, 0x00, 0x79, 0x35, 0xe3, 0xe8, 0x94, 0x90, 0x3c, 0x23, 0x8f, 0x5b, 0x8e, 0xcc, 0x39, 0x82 + , 0x2e, 0xdf, 0xbc, 0xcb, 0x66, 0xe9, 0xe4, 0x3e, 0xad, 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 + , 0x01, 0x60, 0x17, 0x53, 0x7a, 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, 0x40, 0x00, 0x00, 0x8a, 0x62, 0xdd, 0xe7, 0xfd, 0x60, 0x17, 0x53, 0x7b, 0x01, 0x02, 0x0b + , 0x5d, 0x1b, 0x41, 0x29, 0x50, 0xe7, 0x79, 0x39, 0x76, 0xc2, 0xd0, 0xbd, 0x54, 0x2c, 0x1c, 0x2b + , 0x78, 0x25, 0x8b, 0xd6, 0x2d, 0x70, 0x09, 0x73, 0xb7, 0x1c, 0xe4, 0xa2, 0x88, 0x98, 0xb6, 0x44 + , 0xa5, 0x33, 0x0a, 0x98, 0xdc, 0x63, 0xd1, 0x7b, 0x99, 0x49, 0xf2, 0x29, 0xe6, 0x6f, 0x58, 0xc6 + , 0xcb, 0x5a, 0x74, 0xa0, 0xdf, 0xa7, 0x74, 0x84, 0xd5, 0xe1, 0x0f, 0x03, 0x7d, 0xb6, 0xcd, 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, 0x60, 0x17, 0x53, 0x7b, 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, 0x8e, 0x76, 0xce, 0x94, 0x8e, 0x00 + , 0x00, 0x00, 0x00, 0x10, 0x06, 0x00, 0x8a, 0x01, 0x02, 0x25, 0x9f, 0x23, 0x6a, 0xbd, 0x5b, 0x6a + , 0x6b, 0x0f, 0x77, 0xaa, 0xce, 0xe9, 0xe1, 0x6d, 0xe3, 0xfb, 0xcd, 0x10, 0xa6, 0x2b, 0xb6, 0x15 + , 0x0c, 0xdf, 0xa1, 0xde, 0x79, 0x82, 0x99, 0xb1, 0x83, 0x47, 0x44, 0xf7, 0x20, 0xbc, 0x49, 0x11 + , 0x59, 0x58, 0x25, 0x63, 0x76, 0x01, 0x69, 0x27, 0xdc, 0xb3, 0x6c, 0x68, 0xc8, 0x5f, 0xae, 0x13 + , 0xaa, 0x46, 0xcc, 0xe9, 0x68, 0x03, 0x2a, 0xd3, 0x21, 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 + , 0x01, 0x60, 0x17, 0x53, 0x7f, 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, 0x8e, 0x4b, 0x8b, 0x0b, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x10, 0x06, 0x00 + , 0x8a, 0x01, 0x02, 0x7b, 0xd9, 0xa5, 0xe6, 0xfb, 0x26, 0xe2, 0xe1, 0xcb, 0x9a, 0x68, 0xdf, 0x50 + , 0x6c, 0x14, 0xcb, 0x5a, 0x2d, 0x12, 0x40, 0x94, 0x5e, 0xa4, 0x2d, 0xe9, 0x2a, 0x29, 0x48, 0xd5 + , 0xd0, 0x2e, 0xd9, 0x0c, 0xdc, 0xba, 0xe2, 0x74, 0x6e, 0xfb, 0xca, 0x77, 0xea, 0xe9, 0xa2, 0xce + , 0x9a, 0xa8, 0x42, 0x09, 0xa3, 0xa3, 0xae, 0x0e, 0x0f, 0xcc, 0xd3, 0x93, 0xd5, 0xcc, 0x38, 0x76 + , 0xd3, 0x58, 0xcc, 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, 0x01, 0x60, 0x17, 0x53, 0x7f, 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 +}; + +/* 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 ? "->" : "", + type_to_string(this_ctx, struct short_channel_id, &scid)); + } + delivered = flows[i]->amounts[tal_count(flows[i]->amounts)-1]; + if (!amount_msat_sub(&fee, flows[i]->amounts[0], delivered)) + abort(); + tal_append_fmt(&buff," prob %.2f, %s delivered with fee %s\n", + flows[i]->success_prob, + type_to_string(this_ctx, struct amount_msat, &delivered), + type_to_string(this_ctx, struct amount_msat, &fee)); + } + tal_free(this_ctx); + return buff; +} + +int main(int argc, char *argv[]) +{ + int fd; + char *gossfile; + struct gossmap *gossmap; + struct node_id l1, l2, l3; + struct flow **flows; + struct short_channel_id scid12, scid23; + struct chan_extra_map *chan_extra_map; + + common_setup(argv[0]); + + fd = tmpdir_mkstemp(tmpctx, "run-not_mcf.XXXXXX", &gossfile); + assert(write_all(fd, canned_map, sizeof(canned_map))); + + gossmap = gossmap_load(tmpctx, gossfile, NULL); + assert(gossmap); + remove(gossfile); + + /* There is a public channel 2<->3 (103x1x0), and private + * 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("110x1x1", 7, &scid12)); + assert(short_channel_id_from_str("103x1x0", 7, &scid23)); + + chan_extra_map = tal(tmpctx, struct chan_extra_map); + chan_extra_map_init(chan_extra_map); + uncertainty_network_update(gossmap,chan_extra_map); + + flows = minflow(tmpctx, gossmap, + gossmap_find_node(gossmap, &l1), + gossmap_find_node(gossmap, &l3), + chan_extra_map, NULL, + /* Half the capacity */ + AMOUNT_MSAT(500000000), + /* max_fee = */ AMOUNT_MSAT(1000000), // 1k sats + /* min probability = */ 0.1, + /* delay fee factor = */ 1, + /* base fee penalty */ 1, + /* prob cost factor = */ 10); + commit_flow_set(gossmap,chan_extra_map,flows); + debug_info("%s\n", + print_flows(tmpctx,"Flow via single path l1->l2->l3", gossmap, flows)); + + + + /* Should go 1->2->3 */ + assert(tal_count(flows) == 1); + 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); + assert(amount_msat_eq(flows[0]->amounts[1], AMOUNT_MSAT(500000000))); + /* fee_base_msat == 20, fee_proportional_millionths == 1000 */ + assert(amount_msat_eq(flows[0]->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.250); + + + /* Should have filled in some extra data! */ + struct chan_extra *ce = chan_extra_map_get(chan_extra_map, 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 = chan_extra_map_get(chan_extra_map, 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))); + + // /* 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, NULL)); + assert(gossmap_local_updatechan(mods, &scid13, + AMOUNT_MSAT(0), + AMOUNT_MSAT(400000000), + 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); + + /* Clear that */ + remove_completed_flow_set(gossmap, chan_extra_map, flows); + + /* The local chans have no "capacity", so set it manually. */ + new_chan_extra(chan_extra_map, 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]); + + /* 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), + chan_extra_map, NULL, + /* 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% + /* delay fee factor = */ 1, + /* base fee penalty */ 1, + /* prob cost factor = */ 10); + debug_info("%s\n", + print_flows(tmpctx,"Flow via two paths, high mu", gossmap, flows2)); + assert(tal_count(flows2) == 2); + assert(tal_count(flows2[0]->path) == 1); + assert(tal_count(flows2[1]->path) == 2); + + // /* Sends more via 1->3, since it's more expensive (but lower prob) */ + assert(amount_msat_greater(flows2[0]->amounts[0], flows2[1]->amounts[0])); + assert(flows2[0]->success_prob < flows2[1]->success_prob); + + /* Delivered amount must be the total! */ + assert(flows2[0]->amounts[0].millisatoshis + + flows2[1]->amounts[1].millisatoshis == 500000000); + + // /* But in total it's more expensive! */ + assert(flows2[0]->amounts[0].millisatoshis + flows2[1]->amounts[0].millisatoshis + > flows2[0]->amounts[0].millisatoshis - flows2[1]->amounts[0].millisatoshis); + + common_shutdown(); +} diff --git a/plugins/renepay/test/run-payflow_map.c b/plugins/renepay/test/run-payflow_map.c new file mode 100644 index 000000000..e98eea191 --- /dev/null +++ b/plugins/renepay/test/run-payflow_map.c @@ -0,0 +1,96 @@ +/* Eduardo: testing payflow_map. + * */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define RENEPAY_UNITTEST +#include + +static void destroy_payflow( + struct pay_flow *p, + struct payflow_map * map) +{ + printf("calling %s with %s\n", + __PRETTY_FUNCTION__, + fmt_payflow_key(tmpctx,&p->key)); + payflow_map_del(map, p); +} +static struct pay_flow* new_payflow( + const tal_t *ctx, + struct sha256 * payment_hash, + u64 gid, + u64 pid) +{ + struct pay_flow *p = tal(ctx,struct pay_flow); + + p->payment=NULL; + p->key.payment_hash=payment_hash; + p->key.groupid = gid; + p->key.partid = pid; + + return p; +} + +static void valgrind_ok1(void) +{ + const char seed[] = "seed"; + struct sha256 hash; + + sha256(&hash,seed,sizeof(seed)); + + tal_t *this_ctx = tal(tmpctx,tal_t); + + struct payflow_map *map + = tal(this_ctx, struct payflow_map); + + payflow_map_init(map); + + { + tal_t *local_ctx = tal(this_ctx,tal_t); + + struct pay_flow *p1 = new_payflow(local_ctx, + &hash,1,1); + struct pay_flow *p2 = new_payflow(local_ctx, + &hash,2,3); + + printf("key1 = %s\n",fmt_payflow_key(local_ctx,&p1->key)); + printf("key1 = %s\n",fmt_payflow_key(local_ctx,&p2->key)); + printf("key hash 1 = %ld\n",payflow_key_hash(p1->key)); + printf("key hash 2 = %ld\n",payflow_key_hash(p2->key)); + + payflow_map_add(map,p1); tal_add_destructor2(p1,destroy_payflow,map); + payflow_map_add(map,p2); tal_add_destructor2(p2,destroy_payflow,map); + + struct pay_flow *q1 = payflow_map_get(map,payflow_key(&hash,1,1)); + struct pay_flow *q2 = payflow_map_get(map,payflow_key(&hash,2,3)); + + assert(payflow_key_hash(q1->key)==payflow_key_hash(p1->key)); + assert(payflow_key_hash(q2->key)==payflow_key_hash(p2->key)); + + tal_free(local_ctx); + } + + tal_free(this_ctx); + +} +int main(int argc, char *argv[]) +{ + common_setup(argv[0]); + valgrind_ok1(); + common_shutdown(); +} + diff --git a/plugins/renepay/test/run-testflow.c b/plugins/renepay/test/run-testflow.c new file mode 100644 index 000000000..276ddd8b6 --- /dev/null +++ b/plugins/renepay/test/run-testflow.c @@ -0,0 +1,696 @@ +#include "config.h" +#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 "../uncertainty_network.c" +#include "../mcf.c" + +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_complete(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); + 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_sat cap; + struct amount_msat sum_min1_max0,sum_min0_max1; + + // check the bounds channel 1--2 + c = gossmap_find_chan(gossmap,&scid12); + assert(gossmap_chan_get_capacity(gossmap,c,&cap)); + + h0 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,0); + 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); + 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_sat(h0->known_max,cap)); + assert(amount_msat_less_eq(h1->known_min,h1->known_max)); + assert(amount_msat_less_eq_sat(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_sat(sum_min1_max0,cap)); + assert(amount_msat_eq_sat(sum_min0_max1,cap)); + + // check the bounds channel 2--3 + c = gossmap_find_chan(gossmap,&scid23); + assert(gossmap_chan_get_capacity(gossmap,c,&cap)); + + h1 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,1); + 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); + 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_sat(h0->known_max,cap)); + assert(amount_msat_less_eq(h1->known_min,h1->known_max)); + assert(amount_msat_less_eq_sat(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_sat(sum_min1_max0,cap)); + assert(amount_msat_eq_sat(sum_min0_max1,cap)); + + // check the bounds channel 3--4 + c = gossmap_find_chan(gossmap,&scid34); + assert(gossmap_chan_get_capacity(gossmap,c,&cap)); + + h0 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,0); + 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); + 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_sat(h0->known_max,cap)); + assert(amount_msat_less_eq(h1->known_min,h1->known_max)); + assert(amount_msat_less_eq_sat(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_sat(sum_min1_max0,cap)); + assert(amount_msat_eq_sat(sum_min0_max1,cap)); + + // check the bounds channel 4--5 + c = gossmap_find_chan(gossmap,&scid45); + assert(gossmap_chan_get_capacity(gossmap,c,&cap)); + + h0 = get_chan_extra_half_by_chan_verify(gossmap,chan_extra_map,c,0); + 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); + 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_sat(h0->known_max,cap)); + assert(amount_msat_less_eq(h1->known_min,h1->known_max)); + assert(amount_msat_less_eq_sat(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_sat(sum_min1_max0,cap)); + assert(amount_msat_eq_sat(sum_min0_max1,cap)); + + struct flow *F = tal(this_ctx,struct flow); + struct amount_msat deliver; + + // flow 1->2 + F->path = tal_arr(F,struct gossmap_chan const *,1); + F->dirs = tal_arr(F,int,1); + F->path[0]=gossmap_find_chan(gossmap,&scid12); + F->dirs[0]=0; + deliver = AMOUNT_MSAT(250000000); + flow_complete(F,gossmap,chan_extra_map,deliver); + assert(amount_msat_eq(F->amounts[0],deliver)); + assert(fabs(F->success_prob - 0.5)4->5 + F->path = tal_arr(F,struct gossmap_chan const *,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); + flow_complete(F,gossmap,chan_extra_map,deliver); + assert(amount_msat_eq(F->amounts[0],amount_msat(250050016))); + assert(fabs(F->success_prob - 1.)3->4->5 + F->path = tal_arr(F,struct gossmap_chan const *,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); + flow_complete(F,gossmap,chan_extra_map,deliver); + assert(amount_msat_eq(F->amounts[0],amount_msat(250087534))); + assert(fabs(F->success_prob - 1. + 250.087534/2000)2->3->4->5 + F->path = tal_arr(F,struct gossmap_chan const *,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); + flow_complete(F,gossmap,chan_extra_map,deliver); + assert(amount_msat_eq(F->amounts[0],amount_msat(250112544))); + assert(fabs(F->success_prob - 0.43728117) +#include + +static bool chan_extra_check_invariants(struct chan_extra *ce) +{ + bool all_ok = true; + for(int i=0;i<2;++i) + { + all_ok &= amount_msat_less_eq(ce->half[i].known_min, + ce->half[i].known_max); + all_ok &= amount_msat_less_eq(ce->half[i].known_max, + ce->capacity); + } + struct amount_msat diff_cb,diff_ca; + + all_ok &= amount_msat_sub(&diff_cb,ce->capacity,ce->half[1].known_max); + all_ok &= amount_msat_sub(&diff_ca,ce->capacity,ce->half[1].known_min); + + all_ok &= amount_msat_eq(ce->half[0].known_min,diff_cb); + all_ok &= amount_msat_eq(ce->half[0].known_max,diff_ca); + return all_ok; +} + + +/* Checks the entire uncertainty network for invariant violations. */ +bool uncertainty_network_check_invariants(struct chan_extra_map *chan_extra_map) +{ + bool all_ok = true; + + struct chan_extra_map_iter it; + for(struct chan_extra *ce = chan_extra_map_first(chan_extra_map,&it); + ce && all_ok; + ce=chan_extra_map_next(chan_extra_map,&it)) + { + all_ok &= chan_extra_check_invariants(ce); + } + + return all_ok; +} + +static void add_hintchan( + struct chan_extra_map *chan_extra_map, + struct renepay * renepay, + 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) +{ + int dir = node_id_cmp(src, dst) < 0 ? 0 : 1; + + struct chan_extra *ce = chan_extra_map_get(chan_extra_map, + scid); + if(!ce) + { + /* this channel is not public, we don't know his capacity */ + // TODO(eduardo): one possible solution is set the capacity to + // MAX_CAP and the state to [0,MAX_CAP]. Alternatively we set + // the capacity to amoung and state to [amount,amount]. + ce = new_chan_extra(chan_extra_map, + scid, + MAX_CAP); + /* FIXME: features? */ + gossmap_local_addchan(renepay->local_gossmods, + src, dst, &scid, NULL); + gossmap_local_updatechan(renepay->local_gossmods, + &scid, + /* We assume any HTLC is allowed */ + AMOUNT_MSAT(0), MAX_CAP, + fee_base_msat, fee_proportional_millionths, + cltv_expiry_delta, + true, + dir); + } + + /* It is wrong to assume that this channel has sufficient capacity! + * Doing so leads to knowledge updates in which the known min liquidity + * is greater than the channel's capacity. */ + // chan_extra_can_send(chan_extra_map,scid,dir,amount); +} + +/* Add routehints provided by bolt11 */ +void uncertainty_network_add_routehints( + struct chan_extra_map *chan_extra_map, + struct renepay *renepay) +{ + struct payment const * const p = renepay->payment; + struct bolt11 *b11; + char *fail; + + b11 = + bolt11_decode(tmpctx, p->invstr, + plugin_feature_set(renepay->cmd->plugin), + p->description, chainparams, &fail); + if (b11 == NULL) + debug_err("add_routehints: Invalid bolt11: %s", fail); + + for (size_t i = 0; i < tal_count(b11->routes); i++) { + /* Each one, presumably, leads to the destination */ + const struct route_info *r = b11->routes[i]; + const struct node_id *end = & p->destination; + for (int j = tal_count(r)-1; j >= 0; j--) { + add_hintchan( + chan_extra_map, + renepay, &r[j].pubkey, end, + r[j].cltv_expiry_delta, + r[j].short_channel_id, + r[j].fee_base_msat, + r[j].fee_proportional_millionths); + end = &r[j].pubkey; + } + } +} + +/* Mirror the gossmap in the public uncertainty network. + * result: Every channel in gossmap must have associated data in chan_extra_map, + * while every channel in chan_extra_map is also registered in gossmap. + * */ +void uncertainty_network_update( + const struct gossmap *gossmap, + struct chan_extra_map *chan_extra_map) +{ + const tal_t* this_ctx = tal(tmpctx,tal_t); + + // For each chan in chan_extra_map remove if not in the gossmap + struct short_channel_id *del_list + = tal_arr(this_ctx,struct short_channel_id,0); + + 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)) + { + struct gossmap_chan * chan = gossmap_find_chan(gossmap,&ce->scid); + /* Only if the channel is not in the gossmap and there are not + * HTLCs pending we can remove it. */ + if(!chan && !chan_extra_is_busy(ce)) + { + // TODO(eduardo): is this efficiently implemented? + // otherwise i'll use a ccan list + tal_arr_expand(&del_list, ce->scid); + } + } + + for(size_t i=0;ipath_scids); i++) + { + chan_extra_sent_success( + chan_extra_map, + flow->path_scids[i], + flow->path_dirs[i], + flow->amounts[i]); + } +} +/* All parts up to erridx succeeded, so we know something about min + * capacity! */ +void uncertainty_network_channel_can_send( + struct chan_extra_map * chan_extra_map, + struct pay_flow *flow, + u32 erridx) +{ + for (size_t i = 0; i < erridx; i++) + { + chan_extra_can_send(chan_extra_map, + flow->path_scids[i], + flow->path_dirs[i], + + /* This channel can send all that was + * commited in HTLCs. + * Had we removed the commited amount then + * we would have to put here flow->amounts[i]. */ + AMOUNT_MSAT(0)); + } +} + +/* listpeerchannels gives us the certainty on local channels' capacity. Of course, + * this is racy and transient, but better than nothing! */ +bool uncertainty_network_update_from_listpeerchannels( + struct chan_extra_map * chan_extra_map, + struct node_id my_id, + struct renepay * renepay, + const char *buf, + const jsmntok_t *toks) +{ + struct payment * const p = renepay->payment; + const jsmntok_t *channels, *channel; + size_t i; + + if (json_get_member(buf, toks, "error")) + goto malformed; + + channels = json_get_member(buf, toks, "channels"); + if (!channels) + goto malformed; + + json_for_each_arr(i, channel, channels) { + struct short_channel_id scid; + const jsmntok_t *scidtok = json_get_member(buf, channel, "short_channel_id"); + /* If channel is still opening, this won't be there. + * Also it won't be in the gossmap, so there is + * no need to mark it as disabled. */ + if (!scidtok) + continue; + if (!json_to_short_channel_id(buf, scidtok, &scid)) + goto malformed; + + bool connected; + if(!json_to_bool(buf, + json_get_member(buf,channel,"peer_connected"), + &connected)) + goto malformed; + + if (!connected) { + debug_paynote(p, "local channel %s disabled:" + " peer disconnected", + type_to_string(tmpctx, + struct short_channel_id, + &scid)); + tal_arr_expand(&renepay->disabled, scid); + continue; + } + + const jsmntok_t *spendabletok, *dirtok,*statetok, *totaltok, + *peeridtok; + struct amount_msat spendable,capacity; + int dir; + + const struct node_id src=my_id; + struct node_id dst; + + spendabletok = json_get_member(buf, channel, "spendable_msat"); + dirtok = json_get_member(buf, channel, "direction"); + statetok = json_get_member(buf, channel, "state"); + totaltok = json_get_member(buf, channel, "total_msat"); + peeridtok = json_get_member(buf,channel,"peer_id"); + + if(spendabletok==NULL || dirtok==NULL || statetok==NULL || + totaltok==NULL || peeridtok==NULL) + goto malformed; + if (!json_to_msat(buf, spendabletok, &spendable)) + goto malformed; + if (!json_to_msat(buf, totaltok, &capacity)) + goto malformed; + if (!json_to_int(buf, dirtok,&dir)) + goto malformed; + if(!json_to_node_id(buf,peeridtok,&dst)) + goto malformed; + + /* Don't report opening/closing channels */ + if (!json_tok_streq(buf, statetok, "CHANNELD_NORMAL")) { + tal_arr_expand(&renepay->disabled, scid); + continue; + } + + struct chan_extra *ce = chan_extra_map_get(chan_extra_map, + scid); + + if(!ce) + { + /* this channel is not public, but it belongs to us */ + ce = new_chan_extra(chan_extra_map, + scid, + capacity); + /* FIXME: features? */ + gossmap_local_addchan(renepay->local_gossmods, + &src, &dst, &scid, NULL); + gossmap_local_updatechan(renepay->local_gossmods, + &scid, + + /* TODO(eduardo): does it + * matter to consider HTLC + * limits in our own channel? */ + AMOUNT_MSAT(0),capacity, + + /* fees = */0,0, + + /* TODO(eduardo): does it + * matter to set this delay? */ + /*delay=*/0, + true, + dir); + } + + // TODO(eduardo): this includes pending HTLC of previous + // payments! + /* We know min and max liquidity exactly now! */ + chan_extra_set_liquidity(chan_extra_map, + scid,dir,spendable); + } + return true; + +malformed: + return false; +} diff --git a/plugins/renepay/uncertainty_network.h b/plugins/renepay/uncertainty_network.h new file mode 100644 index 000000000..576005fb7 --- /dev/null +++ b/plugins/renepay/uncertainty_network.h @@ -0,0 +1,47 @@ +#ifndef LIGHTNING_PLUGINS_RENEPAY_UNCERTAINTY_NETWORK_H +#define LIGHTNING_PLUGINS_RENEPAY_UNCERTAINTY_NETWORK_H +#include "config.h" +#include +#include +#include +#include + +struct pay_flow; + +/* Checks the entire uncertainty network for invariant violations. */ +bool uncertainty_network_check_invariants(struct chan_extra_map *chan_extra_map); + +/* Add routehints provided by bolt11 */ +void uncertainty_network_add_routehints( + struct chan_extra_map *chan_extra_map, + struct renepay *renepay); + +/* Mirror the gossmap in the public uncertainty network. + * result: Every channel in gossmap must have associated data in chan_extra_map, + * while every channel in chan_extra_map is also registered in gossmap. + * */ +void uncertainty_network_update( + const struct gossmap *gossmap, + struct chan_extra_map *chan_extra_map); + +void uncertainty_network_flow_success( + struct chan_extra_map *chan_extra_map, + struct pay_flow *flow); + +/* All parts up to erridx succeeded, so we know something about min + * capacity! */ +void uncertainty_network_channel_can_send( + struct chan_extra_map * chan_extra_map, + struct pay_flow *flow, + u32 erridx); + +/* listpeerchannels gives us the certainty on local channels' capacity. Of course, + * this is racy and transient, but better than nothing! */ +bool uncertainty_network_update_from_listpeerchannels( + struct chan_extra_map * chan_extra_map, + struct node_id my_id, + struct renepay * renepay, + const char *buf, + const jsmntok_t *toks); + +#endif /* LIGHTNING_PLUGINS_RENEPAY_UNCERTAINTY_NETWORK_H */ diff --git a/tests/test_renepay.py b/tests/test_renepay.py new file mode 100644 index 000000000..748657d5c --- /dev/null +++ b/tests/test_renepay.py @@ -0,0 +1,239 @@ +from fixtures import * # noqa: F401,F403 +from pyln.client import RpcError, Millisatoshi +from utils import only_one, wait_for, mine_funding_to_announce +import pytest +import random +import time + + +def test_simple(node_factory): + '''Testing simply paying a peer.''' + l1, l2 = node_factory.line_graph(2) + inv = l2.rpc.invoice(123000, 'test_renepay', 'description')['bolt11'] + details = l1.rpc.call('renepay', {'invstring': inv}) + assert details['status'] == 'complete' + assert details['amount_msat'] == Millisatoshi(123000) + assert details['destination'] == l2.info['id'] + + +def test_mpp(node_factory): + '''Test paying a remote node using two routes. + 1----2----4 + | | + 3----5----6 + Try paying 1.2M sats from 1 to 6. + ''' + opts = [ + {'disable-mpp': None, 'fee-base': 0, 'fee-per-satoshi': 0}, + ] + l1, l2, l3, l4, l5, l6 = node_factory.get_nodes(6, opts=opts * 6) + node_factory.join_nodes([l1, l2, l4, l6], + wait_for_announce=True, fundamount=1000000) + node_factory.join_nodes([l1, l3, l5, l6], + wait_for_announce=True, fundamount=1000000) + + send_amount = Millisatoshi('1200000sat') + inv = l6.rpc.invoice(send_amount, 'test_renepay', 'description')['bolt11'] + details = l1.rpc.call('renepay', {'invstring': inv}) + assert details['status'] == 'complete' + assert details['amount_msat'] == send_amount + assert details['destination'] == l6.info['id'] + + +def test_errors(node_factory, bitcoind): + opts = [ + {'disable-mpp': None, 'fee-base': 0, 'fee-per-satoshi': 0}, + ] + l1, l2, l3, l4, l5, l6 = node_factory.get_nodes(6, opts=opts * 6) + send_amount = Millisatoshi('21sat') + inv = l6.rpc.invoice(send_amount, 'test_renepay', 'description')['bolt11'] + + failmsg = r'We don\'t have any channels' + with pytest.raises(RpcError, match=failmsg): + l1.rpc.call('renepay', {'invstring': inv}) + node_factory.join_nodes([l1, l2, l4], + wait_for_announce=True, fundamount=1000000) + node_factory.join_nodes([l1, l3, l5], + wait_for_announce=True, fundamount=1000000) + + failmsg = r'Destination is unreacheable in the network gossip.' + with pytest.raises(RpcError, match=failmsg): + l1.rpc.call('renepay', {'invstring': inv}) + + node_factory.join_nodes([l4, l6], + wait_for_announce=True, fundamount=1000000) + node_factory.join_nodes([l5, l6], + wait_for_announce=True, fundamount=1000000) + + l4.rpc.connect(l6.info['id'], 'localhost', l6.port) + l5.rpc.connect(l6.info['id'], 'localhost', l6.port) + + scid46, _ = l4.fundchannel(l6, 10**6, wait_for_active=False) + scid56, _ = l5.fundchannel(l6, 10**6, wait_for_active=False) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4, l5, l6]) + + l1.daemon.wait_for_logs([r'update for channel {}/0 now ACTIVE' + .format(scid46), + r'update for channel {}/1 now ACTIVE' + .format(scid46), + r'update for channel {}/0 now ACTIVE' + .format(scid56), + r'update for channel {}/1 now ACTIVE' + .format(scid56)]) + details = l1.rpc.call('renepay', {'invstring': inv, 'use_shadow': False}) + assert details['status'] == 'complete' + assert details['amount_msat'] == send_amount + assert details['destination'] == l6.info['id'] + + +@pytest.mark.developer("needs to deactivate shadow routing") +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +def test_pay(node_factory): + l1, l2 = node_factory.line_graph(2) + + inv = l2.rpc.invoice(123000, 'test_pay', 'description')['bolt11'] + before = int(time.time()) + details = l1.rpc.call('renepay', {'invstring': inv, 'use_shadow': False}) + after = time.time() + preimage = details['payment_preimage'] + assert details['status'] == 'complete' + assert details['amount_msat'] == Millisatoshi(123000) + assert details['destination'] == l2.info['id'] + assert details['created_at'] >= before + assert details['created_at'] <= after + + invoices = l2.rpc.listinvoices('test_pay')['invoices'] + assert len(invoices) == 1 + invoice = invoices[0] + assert invoice['status'] == 'paid' and invoice['paid_at'] >= before and invoice['paid_at'] <= after + + # Repeat payments are NOPs (if valid): we can hand null. + l1.rpc.call('renepay', {'invstring': inv, 'use_shadow': False}) + # This won't work: can't provide an amount (even if correct!) + with pytest.raises(RpcError): + l1.rpc.call('renepay', {'invstring': inv, 'amount_msat': 123000}) + with pytest.raises(RpcError): + l1.rpc.call('renepay', {'invstring': inv, 'amount_msat': 122000}) + + # Check pay_index is not null + outputs = l2.db_query( + 'SELECT pay_index IS NOT NULL AS q FROM invoices WHERE label="label";') + assert len(outputs) == 1 and outputs[0]['q'] != 0 + + # Check payment of any-amount invoice. + for i in range(5): + label = "any{}".format(i) + inv2 = l2.rpc.invoice("any", label, 'description')['bolt11'] + # Must provide an amount! + with pytest.raises(RpcError): + l1.rpc.call('renepay', {'invstring': inv2, 'use_shadow': False}) + + l1.rpc.call('renepay', {'invstring': inv2, 'use_shadow': False, + 'amount_msat': random.randint(1000, 999999)}) + + # Should see 6 completed payments + assert len(l1.rpc.listsendpays()['payments']) == 6 + + # Test listsendpays indexed by bolt11. + payments = l1.rpc.listsendpays(inv)['payments'] + assert len(payments) == 1 and payments[0]['payment_preimage'] == preimage + + # Make sure they're completely settled, so accounting correct. + wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['htlcs'] == []) + + # Check channels apy summary view of channel activity + apys_1 = l1.rpc.bkpr_channelsapy()['channels_apy'] + apys_2 = l2.rpc.bkpr_channelsapy()['channels_apy'] + + assert apys_1[0]['channel_start_balance_msat'] == apys_2[0]['channel_start_balance_msat'] + assert apys_1[0]['channel_start_balance_msat'] == apys_1[0]['our_start_balance_msat'] + assert apys_2[0]['our_start_balance_msat'] == Millisatoshi(0) + assert apys_1[0]['routed_out_msat'] == apys_2[0]['routed_in_msat'] + assert apys_1[0]['routed_in_msat'] == apys_2[0]['routed_out_msat'] + + +@pytest.mark.developer("needs to deactivate shadow routing") +def test_amounts(node_factory): + ''' + Check that the amount received matches the amount requested in the invoice. + ''' + l1, l2 = node_factory.line_graph(2) + inv = l2.rpc.invoice(Millisatoshi( + 123456), 'test_pay_amounts', 'description')['bolt11'] + + invoice = only_one(l2.rpc.listinvoices('test_pay_amounts')['invoices']) + + assert isinstance(invoice['amount_msat'], Millisatoshi) + assert invoice['amount_msat'] == Millisatoshi(123456) + + l1.rpc.call('renepay', {'invstring': inv, 'use_shadow': False}) + + invoice = only_one(l2.rpc.listinvoices('test_pay_amounts')['invoices']) + assert isinstance(invoice['amount_received_msat'], Millisatoshi) + assert invoice['amount_received_msat'] >= Millisatoshi(123456) + + +@pytest.mark.developer("needs to deactivate shadow routing") +def test_limits(node_factory): + ''' + Topology: + 1----2----4 + | | + 3----5----6 + Try the error messages when paying when: + - the fees are too high, + - CLTV delay is too high, + - probability of success is too low. + ''' + opts = [ + {'disable-mpp': None, 'fee-base': 0, 'fee-per-satoshi': 100}, + ] + l1, l2, l3, l4, l5, l6 = node_factory.get_nodes(6, opts=opts * 6) + node_factory.join_nodes([l1, l2, l4, l6], + wait_for_announce=True, fundamount=1000000) + node_factory.join_nodes([l1, l3, l5, l6], + wait_for_announce=True, fundamount=1000000) + + inv = l4.rpc.invoice('any', 'any', 'description') + l2.rpc.call('pay', {'bolt11': inv['bolt11'], 'amount_msat': 500000000}) + inv = l5.rpc.invoice('any', 'any', 'description') + l3.rpc.call('pay', {'bolt11': inv['bolt11'], 'amount_msat': 500000000}) + + # FIXME: pylightning should define these! + # PAY_STOPPED_RETRYING = 210 + PAY_ROUTE_NOT_FOUND = 205 + + inv = l6.rpc.invoice("any", "any", 'description') + + # Fee too high. + failmsg = r'Fee exceeds our fee budget' + with pytest.raises(RpcError, match=failmsg) as err: + l1.rpc.call( + 'renepay', {'invstring': inv['bolt11'], 'amount_msat': 1000000, 'maxfee': 1}) + assert err.value.error['code'] == PAY_ROUTE_NOT_FOUND + # TODO(eduardo): which error code shall we use here? + + # TODO(eduardo): shall we list attempts in renepay? + # status = l1.rpc.call('renepaystatus', {'invstring':inv['bolt11']})['paystatus'][0]['attempts'] + + failmsg = r'CLTV delay exceeds our CLTV budget' + # Delay too high. + with pytest.raises(RpcError, match=failmsg) as err: + l1.rpc.call( + 'renepay', {'invstring': inv['bolt11'], 'amount_msat': 1000000, 'maxdelay': 0}) + assert err.value.error['code'] == PAY_ROUTE_NOT_FOUND + + inv2 = l6.rpc.invoice("800000sat", "inv2", 'description') + failmsg = r'Probability is too small' + with pytest.raises(RpcError, match=failmsg) as err: + l1.rpc.call( + 'renepay', {'invstring': inv2['bolt11'], 'min_prob_success': '0.5'}) + assert err.value.error['code'] == PAY_ROUTE_NOT_FOUND + + # if we try again we can finish this payment + l1.rpc.call( + 'renepay', {'invstring': inv2['bolt11'], 'min_prob_success': 0}) + invoice = only_one(l6.rpc.listinvoices('inv2')['invoices']) + assert isinstance(invoice['amount_received_msat'], Millisatoshi) + assert invoice['amount_received_msat'] >= Millisatoshi('800000sat')