diff --git a/plugins/renepay/Makefile b/plugins/renepay/Makefile index e35922b13..a6c80ff8c 100644 --- a/plugins/renepay/Makefile +++ b/plugins/renepay/Makefile @@ -1,6 +1,6 @@ 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 \ +PLUGIN_RENEPAY_HDRS := plugins/renepay/pay.h plugins/renepay/pay_flow.h plugins/renepay/flow.h plugins/renepay/mcf.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) diff --git a/plugins/renepay/dijkstra.c b/plugins/renepay/dijkstra.c index d8a2135d1..eab65370c 100644 --- a/plugins/renepay/dijkstra.c +++ b/plugins/renepay/dijkstra.c @@ -1,3 +1,4 @@ +#define NDEBUG 1 #include "config.h" #include diff --git a/plugins/renepay/heap.h b/plugins/renepay/heap.h deleted file mode 100644 index b06cd8f04..000000000 --- a/plugins/renepay/heap.h +++ /dev/null @@ -1,195 +0,0 @@ -#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 index 4629d024b..b74179912 100644 --- a/plugins/renepay/mcf.c +++ b/plugins/renepay/mcf.c @@ -409,11 +409,12 @@ static arc_t channel_idx_to_arc( 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; + /* check that it doesn't overflow */ + assert(arc.chanidx == chan_idx); return arc; } @@ -439,9 +440,26 @@ static void linearize_channel( __LINE__); } + s64 h = extra_half->htlc_total.millisatoshis/1000; /* Raw: linearize_channel */ s64 a = extra_half->known_min.millisatoshis/1000, /* Raw: linearize_channel */ b = 1 + extra_half->known_max.millisatoshis/1000; /* Raw: linearize_channel */ + /* If HTLCs add up to more than the known_max it means we have a + * completely wrong knowledge. */ + // assert(ha it doesn't mean automatically that our + * known_min should have been updated, because we reserve this HTLC + * after sendpay behind the scenes it might happen that sendpay failed + * because of insufficient funds we haven't noticed yet. */ + // assert(h<=a); + + /* We reduce this channel capacity because HTLC are reserving liquidity. */ + a -= h; + b -= h; + a = MAX(a,0); + b = MAX(a+1,b); + capacity[0]=a; cost[0]=0; for(size_t i=1;igossmap, node, j, &half); - // TODO(eduardo): in which case can this be triggered? if (!gossmap_chan_set(c,half)) continue; @@ -818,9 +835,9 @@ static int find_feasible_flow( // 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); + + augment_flow(linear_network,residual_network,source,target,prev,delta); amount -= delta; } @@ -961,9 +978,9 @@ static int optimize_mcf( // 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); + + augment_flow(linear_network,residual_network,source,target,prev,delta); remaining_amount -= delta; // update potentials @@ -1066,7 +1083,6 @@ struct list_data 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 ** @@ -1123,10 +1139,7 @@ static struct flow ** } - size_t num_paths=0; - tal_t *list_ctx = tal(this_ctx,tal_t); - LIST_HEAD(path_list); - struct list_data *ld; + struct flow **flows = tal_arr(ctx,struct flow*,0); // Select all nodes with negative balance and find a flow that reaches a // positive balance node. @@ -1170,7 +1183,7 @@ static struct flow ** } - struct flow *fp = tal(list_ctx,struct flow); + struct flow *fp = tal(this_ctx,struct flow); fp->path = tal_arr(fp,struct gossmap_chan const*,length); fp->dirs = tal_arr(fp,int,length); @@ -1214,22 +1227,16 @@ static struct flow ** // 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++; + // add fp to flows + tal_arr_expand(&flows, fp); } } - // 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) + /* Stablish ownership. */ + for(int i=0;iflow_path); + flows[i] = tal_steal(flows,flows[i]); } - tal_free(this_ctx); return flows; } diff --git a/plugins/renepay/pay.c b/plugins/renepay/pay.c index db0f96703..3a702447d 100644 --- a/plugins/renepay/pay.c +++ b/plugins/renepay/pay.c @@ -20,8 +20,14 @@ // 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 +// 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. + #define INVALID_ID UINT64_MAX #define MAX(a,b) ((a)>(b)? (a) : (b)) +#define MIN(a,b) ((a)<(b)? (a) : (b)) static struct pay_plugin the_pay_plugin; struct pay_plugin * const pay_plugin = &the_pay_plugin; @@ -31,10 +37,6 @@ 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, @@ -92,7 +94,6 @@ static const char *init(struct plugin *p, pay_plugin->ctx = notleak_with_children(tal(p,tal_t)); pay_plugin->plugin = p; - pay_plugin->rexmit_timer=NULL; pay_plugin->last_time = 0; rpc_scan(p, "getinfo", take(json_out_obj(NULL, NULL, NULL)), @@ -132,57 +133,10 @@ static const char *init(struct plugin *p, #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); @@ -202,17 +156,20 @@ static void timer_kick(struct renepay * renepay) { /* Some flows succeeded, we finish the payment. */ case PAYMENT_SUCCESS: + plugin_log(pay_plugin->plugin,LOG_DBG,"status is PAYMENT_SUCCESS"); renepay_success(renepay); break; /* Some flows failed, we retry. */ case PAYMENT_FAIL: + plugin_log(pay_plugin->plugin,LOG_DBG,"status is PAYMENT_FAIL"); payment_assert_delivering_incomplete(p); - try_paying(renepay->cmd,renepay,false); + try_paying(renepay->cmd,renepay,/* always try even if prob is low */ true); break; /* Nothing has returned yet, we have to wait. */ case PAYMENT_PENDING: + plugin_log(pay_plugin->plugin,LOG_DBG,"status is PAYMENT_PENDING"); payment_assert_delivering_all(p); renepay_settimer(renepay); break; @@ -454,6 +411,12 @@ sendpay_flows(struct command *cmd, debug_paynote(p, "Sending out batch of %zu payments", tal_count(flows)); for (size_t i = 0; i < tal_count(flows); i++) { + const u64 path_lengh = tal_count(flows[i]->amounts); + debug_paynote(p, "sendpay flow groupid=%ld, partid=%ld, delivering=%s", + flows[i]->key.groupid, + flows[i]->key.partid, + type_to_string(tmpctx,struct amount_msat, + &flows[i]->amounts[path_lengh-1])); struct out_req *req; req = jsonrpc_request_start(cmd->plugin, cmd, "sendpay", flow_sent, flow_sendpay_failed, @@ -607,7 +570,7 @@ static struct command_result *try_paying(struct command *cmd, /* would you accept unlikely * payments? */ - first_time, + true, /* is entire payment? */ amount_msat_eq(p->total_delivering, AMOUNT_MSAT(0)), @@ -617,7 +580,6 @@ static struct command_result *try_paying(struct command *cmd, // 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, @@ -787,6 +749,10 @@ payment_listsendpays_previous( cmd, LIGHTNINGD, "Unexpected non-array result from listsendpays"); + /* We need two scans of the payments, the first to identify the groupid + * that have pending sendpays and the second to get the maximum partid + * from that group. */ + /* We iterate through all prior sendpays, looking for the * latest group and remembering what its state is. */ json_for_each_arr(i, t, arr) @@ -807,9 +773,12 @@ payment_listsendpays_previous( JSON_SCAN(json_to_msat,&this_msat), JSON_SCAN(json_to_msat,&this_sent)); + if(last_group==INVALID_ID) + last_group = groupid; + + last_group = MAX(last_group,groupid); + /* status could be completed, pending or failed */ - - status = json_get_member(buf, t, "status"); if(json_tok_streq(buf,status,"failed")) @@ -841,32 +810,54 @@ payment_listsendpays_previous( 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)); - } - + last_pending_group_id = MAX(last_pending_group_id,groupid); + first_pending_group_id = MIN(first_pending_group_id,groupid); } } + /* We iterate through all prior sendpays, looking for the + * latest pending group. */ + 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"); + + /* It seems I cannot reuse failed partids for the same groupid, + * therefore let's count them all whatever the status. */ + if(groupid==last_pending_group_id) + last_pending_partid = MAX(last_pending_partid,partid); + + if(groupid == last_pending_group_id && json_tok_streq(buf,status,"pending")) + { + amount_msat_accumulate(&pending_sent,this_sent); + amount_msat_accumulate(&pending_msat,this_msat); + + plugin_log(pay_plugin->plugin,LOG_DBG, + "pending deliver increased by %s", + type_to_string(tmpctx,struct amount_msat,&this_msat)); + } + } + + if (completed) { + /* There are completed sendpays, we don't need to do anything + * but summarize the result. */ struct json_stream *ret = jsonrpc_stream_success(cmd); json_add_preimage(ret, "payment_preimage", &complete_preimage); json_add_string(ret, "status", "complete"); @@ -883,6 +874,9 @@ payment_listsendpays_previous( return command_finished(cmd, ret); } else if (pending) { + assert(last_pending_group_id!=INVALID_ID); + assert(first_pending_group_id!=INVALID_ID); + p->groupid = last_pending_group_id; renepay->next_partid = last_pending_partid+1; @@ -916,6 +910,8 @@ payment_listsendpays_previous( } }else { + /* There are no pending nor completed sendpays, get me the last + * sendpay group. */ p->groupid = (last_group==INVALID_ID ? 1 : (last_group+1)) ; renepay->next_partid=1; } @@ -940,14 +936,14 @@ static struct command_result *json_pay(struct command *cmd, 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 + u64 *base_fee_penalty; + u64 *prob_cost_factor; + u64 *riskfactor_millionths; + u64 *min_prob_success_millionths; bool *use_shadow; #endif @@ -956,15 +952,6 @@ static struct command_result *json_pay(struct command *cmd, 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), @@ -975,6 +962,13 @@ static struct command_result *json_pay(struct command *cmd, p_opt("description", param_string, &description), p_opt("label", param_string, &label), #if DEVELOPER + // 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("riskfactor", param_millionths,&riskfactor_millionths,1), + p_opt_def("min_prob_success", param_millionths, + &min_prob_success_millionths,900000),// default is 90% p_opt_def("use_shadow", param_bool, &use_shadow, true), #endif NULL)) @@ -993,6 +987,23 @@ static struct command_result *json_pay(struct command *cmd, p->label = tal_steal(p,label); p->local_offer_id = tal_steal(p,local_offer_id); + + + /* Please renepay try to give me a reliable payment 90% chances of + * success, once you do, then minimize as much as possible those fees. */ + p->min_prob_success = 0.9; + + /* Default delay_feefactor: how much shall we penalize for delay. */ + p->delay_feefactor = 1e-6; + + /* Default prob_cost_factor: how to convert prob. cost to sats. */ + p->prob_cost_factor = 10; + + /* Default base_fee_penalty: how to convert a base fee into a + * proportional fee. */ + p->base_fee_penalty = 10; + +#if DEVELOPER p->base_fee_penalty = *base_fee_penalty; base_fee_penalty = tal_free(base_fee_penalty); @@ -1004,6 +1015,7 @@ static struct command_result *json_pay(struct command *cmd, p->delay_feefactor = *riskfactor_millionths/1e6; riskfactor_millionths = tal_free(riskfactor_millionths); +#endif p->maxdelay = *maxdelay; maxdelay = tal_free(maxdelay); @@ -1456,7 +1468,7 @@ static void handle_sendpay_failure_flow( u64 errcode; if (!json_to_u64(buf, json_get_member(buf, result, "code"), &errcode)) { - plugin_log(pay_plugin->plugin,LOG_BROKEN, + plugin_err(pay_plugin->plugin, "Failed to get code from sendpay_failure notification" ", received json: %.*s", json_tok_full_len(result), diff --git a/plugins/renepay/pay.h b/plugins/renepay/pay.h index 9161bfdc6..0c7159568 100644 --- a/plugins/renepay/pay.h +++ b/plugins/renepay/pay.h @@ -78,22 +78,17 @@ struct pay_plugin { /* 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; + /* Pending flows have HTLCs (in-flight) liquidity + * attached that is reflected in the uncertainty network. + * When sendpay_fail or sendpay_success notifications arrive + * 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 flows to pay_plugin->ctx so that the + * flows are kept alive. + * + * TODO(eduardo): maybe we should add a check to ensure that pending + * flows are not accumulating ad-infinitum. */ /* It allows us to measure elapsed time * and forget channel information accordingly. */ diff --git a/plugins/renepay/payment.h b/plugins/renepay/payment.h index f1b947f18..cab45eb3d 100644 --- a/plugins/renepay/payment.h +++ b/plugins/renepay/payment.h @@ -82,7 +82,7 @@ struct payment { /* Conversion from prob. cost to millionths */ double prob_cost_factor; - /* linear prob. cost = + /* prob. cost = * - prob_cost_factor * log prob. */ diff --git a/plugins/renepay/test/run-mcf.c b/plugins/renepay/test/run-mcf.c index d0a2dda2d..723d53d60 100644 --- a/plugins/renepay/test/run-mcf.c +++ b/plugins/renepay/test/run-mcf.c @@ -17,6 +17,13 @@ #include #include +static void swap(int *a, int *b) +{ + int temp = *a; + *a = *b; + *b = temp; +} + /* 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 */ @@ -451,20 +458,29 @@ int main(int argc, char *argv[]) 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); + + /* The solution is composed by two paths, one with lenght 1 and the + * other with lenght 2. There is no guaranteed order of the solutions + * returning from minflow, hence we need to test them. */ + int ID1 = 0, ID2 = 1; + if(tal_count(flows2[ID1]->path)==2) + { + swap(&ID1,&ID2); + } + assert(tal_count(flows2[ID1]->path) == 1); + assert(tal_count(flows2[ID2]->path) == 2); // /* Sends more via 1->3, since it's more expensive (but lower prob) */ - assert(amount_msat_greater(flows2[0]->amounts[0], flows2[1]->amounts[0])); - assert(flows2[0]->success_prob < flows2[1]->success_prob); + assert(amount_msat_greater(flows2[ID1]->amounts[0], flows2[ID2]->amounts[0])); + assert(flows2[ID1]->success_prob < flows2[ID2]->success_prob); /* Delivered amount must be the total! */ - assert(flows2[0]->amounts[0].millisatoshis - + flows2[1]->amounts[1].millisatoshis == 500000000); + assert(flows2[ID1]->amounts[0].millisatoshis + + flows2[ID2]->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); + assert(flows2[ID1]->amounts[0].millisatoshis + flows2[ID2]->amounts[0].millisatoshis + > flows2[ID1]->amounts[0].millisatoshis - flows2[ID2]->amounts[0].millisatoshis); common_shutdown(); } diff --git a/tests/test_renepay.py b/tests/test_renepay.py index 748657d5c..f683d0c87 100644 --- a/tests/test_renepay.py +++ b/tests/test_renepay.py @@ -60,11 +60,6 @@ def test_errors(node_factory, bitcoind): 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) @@ -225,15 +220,8 @@ def test_limits(node_factory): 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}) + 'renepay', {'invstring': inv2['bolt11']}) invoice = only_one(l6.rpc.listinvoices('inv2')['invoices']) assert isinstance(invoice['amount_received_msat'], Millisatoshi) assert invoice['amount_received_msat'] >= Millisatoshi('800000sat')