diff --git a/common/test/run-bolt12_merkle-json.c b/common/test/run-bolt12_merkle-json.c index b7519fb7f4f7..bcfc9ee6b0bc 100644 --- a/common/test/run-bolt12_merkle-json.c +++ b/common/test/run-bolt12_merkle-json.c @@ -45,6 +45,9 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* Generated stub for towire_s64 */ +void towire_s64(u8 **pptr UNNEEDED, s64 v UNNEEDED) +{ fprintf(stderr, "towire_s64 called!\n"); abort(); } /* Generated stub for towire_secp256k1_ecdsa_signature */ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, const secp256k1_ecdsa_signature *signature UNNEEDED) @@ -67,9 +70,6 @@ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) /* Generated stub for towire_u64 */ void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) { fprintf(stderr, "towire_u64 called!\n"); abort(); } -/* Generated stub for towire_s64 */ -void towire_s64(u8 **pptr UNNEEDED, s64 v UNNEEDED) -{ fprintf(stderr, "towire_s64 called!\n"); abort(); } /* Generated stub for towire_u8 */ void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) { fprintf(stderr, "towire_u8 called!\n"); abort(); } diff --git a/common/test/run-json_remove.c b/common/test/run-json_remove.c index 526611768a79..64e246e00085 100644 --- a/common/test/run-json_remove.c +++ b/common/test/run-json_remove.c @@ -87,9 +87,6 @@ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u64 */ u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u64 called!\n"); abort(); } -/* Generated stub for fromwire_s64 */ -s64 fromwire_s64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_s64 called!\n"); abort(); } /* Generated stub for fromwire_u8 */ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u8 called!\n"); abort(); } @@ -187,9 +184,6 @@ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) /* Generated stub for towire_u64 */ void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) { fprintf(stderr, "towire_u64 called!\n"); abort(); } -/* Generated stub for towire_s64 */ -void towire_s64(u8 **pptr UNNEEDED, s64 v UNNEEDED) -{ fprintf(stderr, "towire_s64 called!\n"); abort(); } /* Generated stub for towire_u8 */ void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) { fprintf(stderr, "towire_u8 called!\n"); abort(); } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 2b1c0c0aea38..e94054688ff6 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -499,6 +499,10 @@ void json_add_num(struct json_stream *result UNNEEDED, const char *fieldname UNN void json_add_preimage(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const struct preimage *preimage UNNEEDED) { fprintf(stderr, "json_add_preimage called!\n"); abort(); } +/* Generated stub for json_add_s64 */ +void json_add_s64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, + int64_t value UNNEEDED) +{ fprintf(stderr, "json_add_s64 called!\n"); abort(); } /* Generated stub for json_add_secret */ void json_add_secret(struct json_stream *response UNNEEDED, const char *fieldname UNNEEDED, @@ -544,10 +548,6 @@ void json_add_u32(struct json_stream *result UNNEEDED, const char *fieldname UNN void json_add_u64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, uint64_t value UNNEEDED) { fprintf(stderr, "json_add_u64 called!\n"); abort(); } -/* Generated stub for json_add_s64 */ -void json_add_s64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, - int64_t value UNNEEDED) -{ fprintf(stderr, "json_add_s64 called!\n"); abort(); } /* Generated stub for json_add_uncommitted_channel */ void json_add_uncommitted_channel(struct json_stream *response UNNEEDED, const struct uncommitted_channel *uc UNNEEDED, @@ -798,12 +798,6 @@ struct command_result *param_u64(struct command *cmd UNNEEDED, const char *name const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, uint64_t **num UNNEEDED) { fprintf(stderr, "param_u64 called!\n"); abort(); } -/* Generated stub for channel_state_normalish */ -bool channel_state_normalish(const struct channel *channel UNNEEDED) -{ fprintf(stderr, "channel_state_normalish called!\n"); abort(); } -/* Generated stub for channel_state_awaitish */ -bool channel_state_awaitish(const struct channel *channel UNNEEDED) -{ fprintf(stderr, "channel_state_awaitish called!\n"); abort(); } /* Generated stub for peer_any_active_channel */ struct channel *peer_any_active_channel(struct peer *peer UNNEEDED, bool *others UNNEEDED) { fprintf(stderr, "peer_any_active_channel called!\n"); abort(); } diff --git a/plugins/renepay/pay.c b/plugins/renepay/pay.c index ff11721447df..aae578aa65ea 100644 --- a/plugins/renepay/pay.c +++ b/plugins/renepay/pay.c @@ -29,13 +29,7 @@ #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; - -static void timer_kick(struct renepay * renepay); -static struct command_result *try_paying(struct command *cmd, - struct renepay * renepay, - bool first_time); +struct pay_plugin *pay_plugin; void amount_msat_accumulate_(struct amount_msat *dst, struct amount_msat src, @@ -65,25 +59,17 @@ void amount_msat_reduce_(struct amount_msat *dst, #if DEVELOPER static void memleak_mark(struct plugin *p, struct htable *memtable) { - 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_obj(memtable, pay_plugin); 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)); + tal_steal(p, pay_plugin); pay_plugin->plugin = p; pay_plugin->last_time = 0; @@ -101,13 +87,13 @@ static const char *init(struct plugin *p, list_head_init(&pay_plugin->payments); - pay_plugin->chan_extra_map = tal(pay_plugin->ctx,struct chan_extra_map); + pay_plugin->chan_extra_map = tal(pay_plugin,struct chan_extra_map); chan_extra_map_init(pay_plugin->chan_extra_map); - pay_plugin->payflow_map = tal(pay_plugin->ctx,struct payflow_map); + pay_plugin->payflow_map = tal(pay_plugin,struct payflow_map); payflow_map_init(pay_plugin->payflow_map); - pay_plugin->gossmap = gossmap_load(pay_plugin->ctx, + pay_plugin->gossmap = gossmap_load(pay_plugin, GOSSIP_STORE_FILENAME, &num_channel_updates_rejected); @@ -127,68 +113,25 @@ static const char *init(struct plugin *p, return NULL; } - -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: - 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,/* 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; - } -} - /* 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) +static struct pf_result *handle_unhandleable_error(struct pay_flow *pf, + 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); + size_t n = tal_count(pf); /* 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)); + debug_paynote(pf->payment, "%s on route %s", what, flow_path_to_str(tmpctx, pf)); // 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)); + what, flow_path_to_str(tmpctx, pf)); if (n == 1) { - payflow_fail(flow); - return renepay_fail(renepay, PAY_UNPARSEABLE_ONION, - "Got %s from the destination", what); + /* This is a terminal error. */ + return pay_flow_failed_final(pf, PAY_UNPARSEABLE_ONION, what); } /* FIXME: check chan_extra_map, since we might have succeeded though * this node before? */ @@ -201,18 +144,19 @@ static struct command_result *handle_unhandleable_error(struct renepay * renepay /* Assume it's not the destination */ n = pseudorand(n-1); - tal_arr_expand(&renepay->disabled, flow->path_scids[n]); - debug_paynote(p, "... eliminated %s", + tal_arr_expand(&pf->payment->disabled, pf->path_scids[n]); + debug_paynote(pf->payment, "... eliminated %s", type_to_string(tmpctx, struct short_channel_id, - &flow->path_scids[n])); - return NULL; + &pf->path_scids[n])); + + return pay_flow_failed(pf); } /* 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; + struct pay_flow *pf; }; static struct command_result *addgossip_done(struct command *cmd, @@ -221,13 +165,9 @@ static struct command_result *addgossip_done(struct command *cmd, 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); + pay_flow_finished_adding_gossip(adg->pf); tal_free(adg); - renepay_settimer(renepay); return command_still_pending(cmd); } @@ -238,38 +178,32 @@ static struct command_result *addgossip_failure(struct command *cmd, struct addgossip *adg) { + struct payment * payment = adg->pf->payment; 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)", + debug_paynote(payment, "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); + tal_arr_expand(&payment->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) +static struct pf_result *submit_update(struct pay_flow *pf, + 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 payment *payment = pf->payment; struct out_req *req; - struct addgossip *adg = tal(cmd, struct addgossip); + struct addgossip *adg = tal(pf, 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); + adg->pf = pf; - debug_paynote(p, "... extracted channel_update, telling gossipd"); + debug_paynote(payment, "... 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", @@ -277,7 +211,10 @@ static struct command_result *submit_update(struct command *cmd, addgossip_failure, adg); json_add_hex_talarr(req->js, "message", update); - return send_outreq(pay_plugin->plugin, req); + send_outreq(pay_plugin->plugin, req); + + /* Don't retry until we call pay_flow_finished_adding_gossip! */ + return pay_flow_failed_adding_gossip(pf); } /* Fix up the channel_update to include the type if it doesn't currently have @@ -305,13 +242,11 @@ static u8 *patch_channel_update(const tal_t *ctx, u8 *channel_update TAKES) /* 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) + const u8 *onion_message) { 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. * @@ -342,7 +277,7 @@ static u8 *channel_update_from_onion_error(const tal_t *ctx, static struct command_result *flow_sent(struct command *cmd, const char *buf, const jsmntok_t *result, - struct pay_flow *flow) + struct pay_flow *pf) { plugin_log(pay_plugin->plugin,LOG_DBG,"calling %s",__PRETTY_FUNCTION__); return command_still_pending(cmd); @@ -355,78 +290,72 @@ static struct command_result *flow_sent(struct command *cmd, static struct command_result *flow_sendpay_failed(struct command *cmd, const char *buf, const jsmntok_t *err, - struct pay_flow *flow) + struct pay_flow *pf) { - 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); + struct payment *payment = pf->payment; + enum jsonrpc_errcode errcode; + const char *msg; - /* This is a fail. */ - payment_fail(p); + plugin_log(pay_plugin->plugin,LOG_DBG,"calling %s",__PRETTY_FUNCTION__); - u64 errcode; - const jsmntok_t *msg = json_get_member(buf, err, "message"); + debug_assert(payment); - if (!json_to_u64(buf, json_get_member(buf, err, "code"), &errcode)) - plugin_err(cmd->plugin, "Bad errcode from sendpay: %.*s", + if (json_scan(tmpctx, buf, err, + "{code:%,message:%}", + JSON_SCAN(json_to_jsonrpc_errcode, &errcode), + JSON_SCAN_TAL(tmpctx, json_strdup, &msg))) { + plugin_err(cmd->plugin, "Bad fail 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); + debug_paynote(payment, + "sendpay didn't like first hop, eliminated: %s", msg) /* There is no new knowledge from this kind of failure. * We just disable this scid. */ - tal_arr_expand(&renepay->disabled, flow->path_scids[0]); + tal_arr_expand(&payment->disabled, pf->path_scids[0]); - payflow_fail(flow); + pay_flow_failed(pf); return command_still_pending(cmd); } - -static struct command_result * -sendpay_flows(struct command *cmd, - struct renepay * renepay, - struct pay_flow **flows STEALS) +/* Kick off all pay_flows which are in state PAY_FLOW_NOT_STARTED */ +static void sendpay_new_flows(struct payment *p) { - struct payment * const p = renepay->payment; + struct pay_flow *pf; - 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++) { - const size_t path_lengh = tal_count(flows[i]->amounts); - debug_paynote(p, "sendpay flow groupid=%ld, partid=%ld, delivering=%s, probability=%.3lf", - flows[i]->key.groupid, - flows[i]->key.partid, - type_to_string(tmpctx,struct amount_msat, - &flows[i]->amounts[path_lengh-1]), - flows[i]->success_prob); + list_for_each(&p->flows, pf, list) { + if (pf->state != PAY_FLOW_NOT_STARTED) + continue; + + debug_paynote(p, "sendpay flow groupid=%"PRIu64", partid=%"PRIu64", delivering=%s, probability=%.3lf", + pf->key.groupid, + pf->key.partid, + fmt_amount_msat(tmpctx, payflow_delivered(pf)), + pf->success_prob); struct out_req *req; - req = jsonrpc_request_start(cmd->plugin, cmd, "sendpay", + /* FIXME: We don't actually want cmd to own this sendpay, so we use NULL here, + * but we should use a variant which allows us to set json id! */ + req = jsonrpc_request_start(pay_plugin->plugin, NULL, "sendpay", flow_sent, flow_sendpay_failed, - flows[i]); + pf); json_array_start(req->js, "route"); - for (size_t j = 0; j < tal_count(flows[i]->path_nodes); j++) { + for (size_t j = 0; j < tal_count(pf->path_nodes); j++) { json_object_start(req->js, NULL); json_add_node_id(req->js, "id", - &flows[i]->path_nodes[j]); + &pf->path_nodes[j]); json_add_short_channel_id(req->js, "channel", - &flows[i]->path_scids[j]); + &pf->path_scids[j]); json_add_amount_msat(req->js, "amount_msat", - flows[i]->amounts[j]); + pf->amounts[j]); json_add_num(req->js, "direction", - flows[i]->path_dirs[j]); + pf->path_dirs[j]); json_add_u32(req->js, "delay", - flows[i]->cltv_delays[j]); + pf->cltv_delays[j]); json_add_string(req->js,"style","tlv"); json_object_end(req->js); } @@ -437,7 +366,7 @@ sendpay_flows(struct command *cmd, 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, "partid", pf->key.partid); json_add_u64(req->js, "groupid", p->groupid); if (p->payment_metadata) @@ -451,81 +380,46 @@ sendpay_flows(struct command *cmd, 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]); + send_outreq(pay_plugin->plugin, req); - /* 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); + /* Now you're started! */ + pf->state = PAY_FLOW_IN_PROGRESS; } /* 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) +const char *try_paying(const tal_t *ctx, + struct payment *payment, + enum jsonrpc_errcode *ecode) { - 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"); + assert(payment->status == PAYMENT_PENDING); /* Total feebudget */ - if (!amount_msat_sub(&feebudget, p->maxspend, p->amount)) + if (!amount_msat_sub(&feebudget, payment->maxspend, payment->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)); + type_to_string(tmpctx, struct amount_msat, &payment->maxspend), + type_to_string(tmpctx, struct amount_msat, &payment->amount)); } /* Fees spent so far */ - if (!amount_msat_sub(&fees_spent, p->total_sent, p->total_delivering)) + if (!amount_msat_sub(&fees_spent, payment->total_sent, payment->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)); + type_to_string(tmpctx, struct amount_msat, &payment->total_sent), + type_to_string(tmpctx, struct amount_msat, &payment->total_delivering)); } /* Remaining fee budget. */ @@ -540,14 +434,14 @@ static struct command_result *try_paying(struct command *cmd, } /* How much are we still trying to send? */ - if (!amount_msat_sub(&remaining, p->amount, p->total_delivering)) + if (!amount_msat_sub(&remaining, payment->amount, payment->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)); + type_to_string(tmpctx, struct amount_msat, &payment->amount), + type_to_string(tmpctx, struct amount_msat, &payment->total_delivering)); } // plugin_log(pay_plugin->plugin,LOG_DBG,fmt_chan_extra_map(tmpctx,pay_plugin->chan_extra_map)); @@ -556,58 +450,68 @@ static struct command_result *try_paying(struct command *cmd, /* 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? */ - true, - - /* 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)); + gossmap_apply_localmods(pay_plugin->gossmap, payment->local_gossmods); + err_msg = add_payflows(tmpctx, + payment, + remaining, feebudget, + /* is entire payment? */ + amount_msat_eq(payment->total_delivering, AMOUNT_MSAT(0)), + ecode); + gossmap_remove_localmods(pay_plugin->gossmap, payment->local_gossmods); /* MCF cannot find a feasible route, we stop. */ - if (!pay_flows) - { - return renepay_fail(renepay, PAY_ROUTE_NOT_FOUND, - "Failed to find a route, %s", - err_msg); - } + if (err_msg) + return err_msg; + /* Now begin making payments */ + sendpay_new_flows(payment); - return sendpay_flows(cmd, renepay, pay_flows); + return NULL; +} + +static void destroy_cmd_payment_ptr(struct command *cmd, + struct payment *payment) +{ + assert(payment->cmd == cmd); + payment->cmd = NULL; } static struct command_result *listpeerchannels_done( struct command *cmd, const char *buf, const jsmntok_t *result, - struct renepay *renepay) + struct payment *payment) { plugin_log(pay_plugin->plugin,LOG_DBG,"calling %s",__PRETTY_FUNCTION__); + const char *errmsg; + enum jsonrpc_errcode ecode; + if (!uncertainty_network_update_from_listpeerchannels( pay_plugin->chan_extra_map, pay_plugin->my_id, - renepay, + payment, buf, result)) - return renepay_fail(renepay,LIGHTNINGD, + return command_fail(cmd, 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); + + /* From now on, we keep a record of the payment, so persist it beyond this cmd. */ + tal_steal(pay_plugin->plugin, payment); + /* When we terminate cmd for any reason, clear it from payment so we don't do it again. */ + assert(cmd == payment->cmd); + tal_add_destructor2(cmd, destroy_cmd_payment_ptr, payment); + + /* This looks for a route, and if OK, fires off the sendpay commands */ + errmsg = try_paying(tmpctx, payment, &ecode); + if (errmsg) + return payment_fail(payment, ecode, "%s", errmsg); + + return command_still_pending(cmd); } @@ -699,26 +603,21 @@ payment_listsendpays_previous( struct command *cmd, const char *buf, const jsmntok_t *result, - struct renepay * renepay) + struct payment * payment) { debug_info("calling %s",__PRETTY_FUNCTION__); - struct payment * p = renepay->payment; size_t i; - const jsmntok_t *t, *arr, *err; + const jsmntok_t *t, *arr; - /* Do we have pending sendpays for the previous attempt? */ - bool pending = false; - /* Group ID of the first pending payment, this will be the one + /* Group ID of the 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; + u64 pending_group_id = INVALID_ID; + u64 max_pending_partid=0; + u64 max_group_id = 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; @@ -726,60 +625,40 @@ payment_listsendpays_previous( 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 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. */ + "Unexpected non-array result from listsendpays: %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); - /* 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; + const char *status; // TODO(eduardo): assuming amount_msat is always known. json_scan(tmpctx,buf,t, - "{partid:%" + "{status:%" + ",partid:%" ",groupid:%" ",amount_msat:%" ",amount_sent_msat:%}", + JSON_SCAN_TAL(tmpctx, json_strdup, &status), 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)); - if(last_group==INVALID_ID) - last_group = groupid; - - last_group = MAX(last_group,groupid); + /* If we decide to create a new group, we base it on max_group_id */ + if (groupid > max_group_id) + max_group_id = 1; /* 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")) - { + if (streq(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__); @@ -791,63 +670,21 @@ payment_listsendpays_previous( ",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; - - 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)); - } + complete_parts++; + } else if (streq(status, "pending")) { + /* If we have more than one pending group, something went wrong! */ + if (pending_group_id != INVALID_ID + && groupid != pending_group_id) + return command_fail(cmd, PAY_STATUS_UNEXPECTED, + "Multiple pending groups for this payment?"); + pending_group_id = groupid; + if (partid > max_pending_partid) + max_pending_partid = partid; + } else + assert(streq(status, "failed")); } - - if (completed) { + if (complete_parts != 0) { /* There are completed sendpays, we don't need to do anything * but summarize the result. */ struct json_stream *ret = jsonrpc_stream_success(cmd); @@ -855,57 +692,49 @@ payment_listsendpays_previous( 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_node_id(ret, "destination", &payment->destination); + json_add_sha256(ret, "payment_hash", &payment->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); - + * it twice: payment will be freed with cmd */ 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; + } else if (pending_group_id != INVALID_ID) { + /* Continue where we left off? */ + payment->groupid = pending_group_id; + payment->next_partid = max_pending_partid+1; - p->total_sent = pending_sent; - p->total_delivering = pending_msat; + payment->total_sent = pending_sent; + payment->total_delivering = pending_msat; plugin_log(pay_plugin->plugin,LOG_DBG, "There are pending sendpays to this invoice. " "groupid = %"PRIu64" " "delivering = %s, " "last_partid = %"PRIu64, - last_pending_group_id, - type_to_string(tmpctx,struct amount_msat,&p->total_delivering), - last_pending_partid); + pending_group_id, + type_to_string(tmpctx,struct amount_msat,&payment->total_delivering), + max_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)) + if(amount_msat_greater_eq(payment->total_delivering,payment->amount)) { /* Pending payment already pays the full amount, we * better stop. */ - renepay->payment = tal_free(renepay->payment); - return renepay_fail(renepay, PAY_IN_PROGRESS, + return command_fail(cmd, PAY_IN_PROGRESS, "Payment is pending with full amount already commited"); } }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; + /* FIXME: use groupid 0 to have sendpay assign an unused groupid, + * as this is theoretically racy against other plugins paying the + * same thing! + * *BUT* that means we have to create one flow first, so we + * can match the others. */ + payment->groupid = max_group_id + 1; + payment->next_partid=1; } @@ -913,7 +742,7 @@ payment_listsendpays_previous( /* Get local capacities... */ req = jsonrpc_request_start(cmd->plugin, cmd, "listpeerchannels", listpeerchannels_done, - listpeerchannels_done, renepay); + listpeerchannels_done, payment); return send_outreq(cmd->plugin, req); } @@ -928,16 +757,19 @@ static struct command_result *json_pay(struct command *cmd, u64 invexpiry; struct amount_msat *msat, *invmsat; struct amount_msat *maxfee; + struct sha256 payment_hash; + struct secret *payment_secret; + const u8 *payment_metadata; + struct node_id destination; u32 *maxdelay; u32 *retryfor; - -#if DEVELOPER u64 *base_fee_penalty; u64 *prob_cost_factor; u64 *riskfactor_millionths; u64 *min_prob_success_millionths; bool *use_shadow; -#endif + u16 final_cltv; + const struct route_info **routes = NULL; if (!param(cmd, buf, params, p_req("invstring", param_invstring, &invstr), @@ -966,95 +798,32 @@ static struct command_result *json_pay(struct command *cmd, 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); - - - - /* 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); - - 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); -#endif - - 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)) { + /* We might need to parse invstring to get amount */ + if (!bolt12_has_prefix(invstr)) { struct bolt11 *b11; char *fail; b11 = - bolt11_decode(tmpctx, p->invstr, plugin_feature_set(cmd->plugin), - p->description, chainparams, &fail); + bolt11_decode(tmpctx, invstr, plugin_feature_set(cmd->plugin), + description, chainparams, &fail); if (b11 == NULL) - return renepay_fail(renepay, JSONRPC2_INVALID_PARAMS, + return command_fail(cmd, 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); + destination = b11->receiver_id; + payment_hash = b11->payment_hash; + payment_secret = + tal_dup_or_null(cmd, struct secret, b11->payment_secret); if (b11->metadata) - p->payment_metadata = tal_dup_talarr(p, u8, b11->metadata); + payment_metadata = tal_dup_talarr(cmd, u8, b11->metadata); else - p->payment_metadata = NULL; + payment_metadata = NULL; - p->final_cltv = b11->min_final_cltv_expiry; + final_cltv = b11->min_final_cltv_expiry; /* Sanity check */ if (feature_offered(b11->features, OPT_VAR_ONION) && !b11->payment_secret) @@ -1070,37 +839,40 @@ static struct command_result *json_pay(struct command *cmd, */ if (!b11->description) { if (!b11->description_hash) { - return renepay_fail(renepay, + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid bolt11: missing description"); } - if (!p->description) - return renepay_fail(renepay, + if (!description) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "bolt11 uses description_hash, but you did not provide description parameter"); } + + routes = cast_const2(const struct route_info **, + b11->routes); } else { // TODO(eduardo): check this, compare with `pay` const struct tlv_invoice *b12; char *fail; - b12 = invoice_decode(tmpctx, p->invstr, strlen(p->invstr), + b12 = invoice_decode(tmpctx, invstr, strlen(invstr), plugin_feature_set(cmd->plugin), chainparams, &fail); if (b12 == NULL) - return renepay_fail(renepay, JSONRPC2_INVALID_PARAMS, + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid bolt12: %s", fail); if (!pay_plugin->exp_offers) - return renepay_fail(renepay, JSONRPC2_INVALID_PARAMS, + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "experimental-offers disabled"); if (!b12->offer_node_id) - return renepay_fail(renepay, JSONRPC2_INVALID_PARAMS, + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "invoice missing offer_node_id"); if (!b12->invoice_payment_hash) - return renepay_fail(renepay, JSONRPC2_INVALID_PARAMS, + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "invoice missing payment_hash"); if (!b12->invoice_created_at) - return renepay_fail(renepay, JSONRPC2_INVALID_PARAMS, + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "invoice missing created_at"); if (b12->invoice_amount) { invmsat = tal(cmd, struct amount_msat); @@ -1108,25 +880,24 @@ static struct command_result *json_pay(struct command *cmd, } 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, + node_id_from_pubkey(&destination, b12->offer_node_id); + payment_hash = *b12->invoice_payment_hash; + if (b12->invreq_recurrence_counter && !label) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "recurring invoice requires a label"); /* FIXME payment_secret should be signature! */ { struct sha256 merkle; - p->payment_secret = tal(p, struct secret); + payment_secret = tal(cmd, struct secret); merkle_tlv(b12->fields, &merkle); - memcpy(p->payment_secret, &merkle, sizeof(merkle)); - BUILD_ASSERT(sizeof(*p->payment_secret) == + memcpy(payment_secret, &merkle, sizeof(merkle)); + BUILD_ASSERT(sizeof(*payment_secret) == sizeof(merkle)); } - p->payment_metadata = NULL; + payment_metadata = NULL; /* FIXME: blinded paths! */ - p->final_cltv = 18; + final_cltv = 18; /* BOLT-offers #12: * - if `relative_expiry` is present: * - MUST reject the invoice if the current time since @@ -1143,49 +914,95 @@ static struct command_result *json_pay(struct command *cmd, 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, + if (node_id_eq(&pay_plugin->my_id, &destination)) + return command_fail(cmd, 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, + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "amount_msat parameter unnecessary"); } - p->amount = *invmsat; - tal_free(invmsat); + msat = invmsat; } else { // amount is not written in the invoice if (!msat) { - return renepay_fail(renepay, JSONRPC2_INVALID_PARAMS, + return command_fail(cmd, 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); + struct amount_msat fee = amount_msat_div(*msat, 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); - const u64 now_sec = time_now().ts.tv_sec; if (now_sec > invexpiry) - return renepay_fail(renepay, PAY_INVOICE_EXPIRED, "Invoice expired"); + return command_fail(cmd, PAY_INVOICE_EXPIRED, "Invoice expired"); + +#if !DEVELOPER + /* Please renepay try to give me a reliable payment 90% chances of + * success, once you do, then minimize as much as possible those fees. */ + base_fee_penalty = tal(tmpctx, u64); + *base_fee_penalty = 10; + prob_cost_factor = tal(tmpctx, u64); + *prob_cost_factor = 10; + riskfactor_millionths = tal(tmpctx, u64); + *riskfactor_millionths = 1; + min_prob_success_millionths = tal(tmpctx, u64); + *min_prob_success_millionths = 90; + use_shadow = tal(tmpctx, bool); + *use_shadow = 1; +#endif + + /* Payment is allocated off cmd to start, in case we fail cmd + * (e.g. already in progress, already succeeded). Once it's + * actually started, it persists beyond the command, so we + * tal_steal. */ + struct payment *payment = payment_new(cmd, + cmd, + take(invstr), + take(label), + take(description), + take(local_offer_id), + take(payment_secret), + take(payment_metadata), + &destination, + &payment_hash, + *msat, + *maxfee, + *maxdelay, + *retryfor, + final_cltv, + *base_fee_penalty, + *prob_cost_factor, + *riskfactor_millionths, + *min_prob_success_millionths, + use_shadow); + + /* We immediately add this payment to the payment list. */ + list_add_tail(&pay_plugin->payments, &payment->list); + tal_add_destructor(payment, 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)); + + /* Free parameters which would be considered "leaks" by our fussy memleak code */ + tal_free(msat); + tal_free(maxfee); + tal_free(maxdelay); + tal_free(retryfor); /* To construct the uncertainty network we need to perform the following * steps: @@ -1217,8 +1034,7 @@ static struct command_result *json_pay(struct command *cmd, // 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); + uncertainty_network_add_routehints(pay_plugin->chan_extra_map, routes, payment); if(!uncertainty_network_check_invariants(pay_plugin->chan_extra_map)) plugin_log(pay_plugin->plugin, @@ -1230,126 +1046,43 @@ static struct command_result *json_pay(struct command *cmd, struct out_req *req = jsonrpc_request_start(cmd->plugin, cmd, "listsendpays", payment_listsendpays_previous, - payment_listsendpays_previous, renepay); + payment_listsendpays_previous, payment); - json_add_sha256(req->js, "payment_hash", &p->payment_hash); + json_add_sha256(req->js, "payment_hash", &payment->payment_hash); return send_outreq(cmd->plugin, req); } -static void handle_sendpay_failure_renepay( - struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct renepay *renepay, - struct pay_flow *flow) +/* Terminates flow */ +static struct pf_result *handle_sendpay_failure_payment(struct pay_flow *pf STEALS, + const char *message, + u32 erridx, + enum onion_wire onionerr, + const u8 *raw) { - 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; - } + struct payment *p = pf->payment; + const u8 *update; - if (erridxpath_scids) - && !short_channel_id_eq(&errscid, &flow->path_scids[erridx])) - { + debug_assert(pf); + debug_assert(p); + + /* Final node is usually a hard failure */ + if (erridx == tal_count(pf->path_scids)) { debug_paynote(p, - "erring_index (%d) does not correspond" - "to erring_channel (%s) on route %s", + "onion error %s from final node #%u: %s", + onion_wire_name(onionerr), erridx, - type_to_string(tmpctx,struct short_channel_id,&errscid), - flow_path_to_str(tmpctx,flow)); - goto unhandleable; - } + message); - 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; + if (onionerr == WIRE_MPP_TIMEOUT) { + return pay_flow_failed(pf); + } + + debug_paynote(p,"final destination failure"); + return pay_flow_failed_final(pf, PAY_DESTINATION_PERM_FAIL, message); } + errscid = pf->path_scids[erridx]; debug_paynote(p, "onion error %s from node #%u %s: %s", onion_wire_name(onionerr), @@ -1357,11 +1090,7 @@ static void handle_sendpay_failure_renepay( 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) { + switch (onionerr) { /* These definitely mean eliminate channel */ case WIRE_PERMANENT_CHANNEL_FAILURE: case WIRE_REQUIRED_CHANNEL_FEATURE_MISSING: @@ -1382,8 +1111,8 @@ static void handle_sendpay_failure_renepay( 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; + tal_arr_expand(&p->disabled, errscid); + return pay_flow_failed(pf); /* These can be fixed (maybe) by applying the included channel_update */ case WIRE_AMOUNT_BELOW_MINIMUM: @@ -1393,353 +1122,219 @@ static void handle_sendpay_failure_renepay( 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); + update = channel_update_from_onion_error(tmpctx, raw); if (update) - { - submit_update(cmd, flow, update, errscid); - return; - } + return submit_update(pf, update, errscid); 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; + tal_arr_expand(&p->disabled, errscid); + return pay_flow_failed(pf); case WIRE_TEMPORARY_CHANNEL_FAILURE: - case WIRE_MPP_TIMEOUT: - return; + /* These also contain a channel_update, but in this case it's simply + * advisory, not necessary. */ + update = channel_update_from_onion_error(tmpctx, raw); + if (update) + return submit_update(pf, update, errscid); + + return pay_flow_failed(pf); - /* These are from the final distination: fail */ + /* These should only come from the final distination. */ + case WIRE_MPP_TIMEOUT: 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; + break; } - 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, ""); + debug_paynote(p,"unkown onion error code %u, removing scid %s", + onionerr, + type_to_string(tmpctx,struct short_channel_id,&errscid)); + tal_arr_expand(&p->disabled, errscid); + return pay_flow_failed(pf); } -static void handle_sendpay_failure_flow( - struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct pay_flow *flow) +static void handle_sendpay_failure_flow(struct pay_flow *pf, + const char *msg, + u32 erridx, + u32 onionerr) { - // TODO(eduardo): review with Rusty the level of severity of the - // different cases of error below. - debug_assert(flow); + debug_assert(pf); - struct payment * const p = flow->payment; - payment_fail(p); + struct payment * const p = pf->payment; - u64 errcode; - if (!json_to_u64(buf, json_get_member(buf, result, "code"), &errcode)) - { - plugin_err(pay_plugin->plugin, - "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, + 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); + erridx == tal_count(pf->path_scids) + ? "final" + : type_to_string(tmpctx, struct short_channel_id, &pf->path_scids[erridx]), + msg); /* 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, + pf, erridx); - /* Insufficient funds! */ - if((enum onion_wire)onionerr == WIRE_TEMPORARY_CHANNEL_FAILURE) + /* Insufficient funds (not from final, that's weird!) */ + if((enum onion_wire)onionerr == WIRE_TEMPORARY_CHANNEL_FAILURE + && erridx < tal_count(pf->path_scids)) { 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], + pf->path_scids[erridx], + pf->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]. */ + * we would have to put here pf->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) +/* See if this notification is about one of our flows. */ +static struct pay_flow *pay_flow_from_notification(const char *buf, + const jsmntok_t *obj) { - 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; + const char *err; + + /* Single part payment? No partid */ + key.partid = 0; + err = json_scan(tmpctx, buf, obj, "{partid?:%,groupid:%,payment_hash:%}", + JSON_SCAN(json_to_u64, &key.partid), + JSON_SCAN(json_to_u64, &key.groupid), + JSON_SCAN(json_to_sha256, &key.payment_hash)); + if (err) { + plugin_err(pay_plugin->plugin, + "Missing fields (%s) in notification: %.*s", + err, + json_tok_full_len(obj), + json_tok_full(buf, obj)); } - // 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)); + return payflow_map_get(pay_plugin->payflow_map, &key); +} - 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( +static struct command_result *notification_sendpay_success( struct command *cmd, const char *buf, const jsmntok_t *params) { - struct pay_flow *flow = NULL; + struct pay_flow *pf; + struct preimage preimage; + const char *err; + const jsmntok_t *sub = json_get_member(buf, params, "sendpay_success"); - 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)); + pf = pay_flow_from_notification(buf, sub); + if (!pf) + return notification_handled(cmd); - 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)); + err = json_scan(tmpctx, buf, sub, "{payment_preimage:%}", + JSON_SCAN(json_to_preimage, &preimage)); + if (err) { + plugin_err(pay_plugin->plugin, + "Bad payment_preimage (%s) in sendpay_success: %.*s", + err, + json_tok_full_len(params), + json_tok_full(buf, params)); + } + // 2. update information + uncertainty_network_flow_success(pay_plugin->chan_extra_map, pf); - // 1. generate the key of this payflow - struct payflow_key key; - key.payment_hash = tal(tmpctx,struct sha256); + // 3. mark as success (frees pf) + pay_flow_succeeded(pf, &preimage); - 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; + return notification_handled(cmd); +} + +/* Dummy return ensures all paths call pay_flow_* to close flow! */ +static struct pf_result *sendpay_failure(struct pay_flow *pf, + enum jsonrpc_errcode errcode, + const char *buf, + const jsmntok_t *sub) +{ + const char *msg, *err; + u32 erridx, onionerr; + const u8 *raw; + + /* Only one code is really actionable */ + switch (errcode) { + case PAY_UNPARSEABLE_ONION: + debug_paynote(pf->payment, "Unparsable onion reply on route %s", + flow_path_to_str(tmpctx, pf)); + return handle_unhandleable_error(pf, "Unparsable onion reply"); + + case PAY_TRY_OTHER_ROUTE: + break; + case PAY_DESTINATION_PERM_FAIL: + break; + default: + return pay_flow_failed_final(pf, + errcode, + "Unexpected errorcode from sendpay_failure"); } - 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; + + /* Extract remaining fields for feedback */ + err = json_scan(tmpctx, buf, sub, + "{message:%" + ",data:{erring_index:%" + ",failcode:%" + ",raw_message:%}}", + JSON_SCAN_TAL(tmpctx, json_strdup, &msg), + JSON_SCAN(json_to_u32, &erridx), + JSON_SCAN(json_to_u32, &onionerr), + JSON_SCAN_TAL(tmpctx, json_tok_bin_from_hex, &raw)); + if (err) + return handle_unhandleable_error(pf, err); + + /* Answer must be sane: but note, erridx can be final node! */ + if (erridx > tal_count(pf->path_scids)) { + plugin_err(pay_plugin->plugin, + "Erring channel %u/%zu in path %s", + erridx, tal_count(pf->path_scids), + flow_path_to_str(tmpctx, pf)); } - // 3. process failure - handle_sendpay_failure_flow(cmd,buf,resulttok,flow); + handle_sendpay_failure_flow(pf, msg, erridx, onionerr); - // there is possibly a pending renepay command for this flow - struct renepay * const renepay = flow->payment->renepay; + return handle_sendpay_failure_payment(pf, msg, erridx, onionerr, raw); +} - if(renepay) - handle_sendpay_failure_renepay(cmd,buf,resulttok,renepay,flow); +static struct command_result *notification_sendpay_failure( + struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + struct pay_flow *pf; + const char *err; + enum jsonrpc_errcode errcode; + const jsmntok_t *sub = json_get_member(buf, params, "sendpay_failure"); + + pf = pay_flow_from_notification(buf, json_get_member(buf, sub, "data")); + if (!pf) + return notification_handled(cmd); + + err = json_scan(tmpctx, buf, sub, "{code:%}", + JSON_SCAN(json_to_jsonrpc_errcode, &errcode)); + if (err) { + plugin_err(pay_plugin->plugin, + "Bad code (%s) in sendpay_failure: %.*s", + err, + json_tok_full_len(params), + json_tok_full(buf, params)); + } - done: - if(flow) payflow_fail(flow); + sendpay_failure(pf, errcode, buf, sub); return notification_handled(cmd); } @@ -1778,6 +1373,11 @@ static const struct plugin_notification notifications[] = { int main(int argc, char *argv[]) { setup_locale(); + + /* Most gets initialized in init(), but set debug options here. */ + pay_plugin = tal(NULL, struct pay_plugin); + pay_plugin->debug_mcf = pay_plugin->debug_payflow = false; + plugin_main( argv, init, @@ -1796,7 +1396,5 @@ int main(int argc, char *argv[]) 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 index 0c7159568f50..6a2c90e89d6c 100644 --- a/plugins/renepay/pay.h +++ b/plugins/renepay/pay.h @@ -69,15 +69,12 @@ struct pay_plugin { /* Per-channel metadata: some persists between payments */ struct chan_extra_map *chan_extra_map; - /* Pending senpays. */ + /* Pending sendpays (to match notifications to). */ 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; /* Pending flows have HTLCs (in-flight) liquidity * attached that is reflected in the uncertainty network. * When sendpay_fail or sendpay_success notifications arrive @@ -96,7 +93,7 @@ struct pay_plugin { }; /* Set in init */ -extern struct pay_plugin * const pay_plugin; +extern struct pay_plugin *pay_plugin; /* Accumulate or panic on overflow */ #define amount_msat_accumulate(dst, src) \ @@ -113,4 +110,8 @@ void amount_msat_reduce_(struct amount_msat *dst, const char *dstname, const char *srcname); +/* Returns NULL if OK, otherwise an error msg and sets *ecode */ +const char *try_paying(const tal_t *ctx, + struct payment *payment, + enum jsonrpc_errcode *ecode); #endif /* LIGHTNING_PLUGINS_RENEPAY_PAY_H */ diff --git a/plugins/renepay/pay_flow.c b/plugins/renepay/pay_flow.c index a1fed1076ca7..7454bf626fc8 100644 --- a/plugins/renepay/pay_flow.c +++ b/plugins/renepay/pay_flow.c @@ -35,6 +35,70 @@ #define MAX_SHADOW_LEN 3 +static void remove_htlc_payflow( + struct chan_extra_map *chan_extra_map, + struct pay_flow *pf) +{ + for (size_t i = 0; i < tal_count(pf->path_scids); i++) { + struct chan_extra_half *h = get_chan_extra_half_by_scid( + chan_extra_map, + pf->path_scids[i], + pf->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, pf->amounts[i])) + { + plugin_err(pay_plugin->plugin, + "%s could not substract HTLC amounts, " + "half total htlc amount = %s, " + "pf->amounts[%lld] = %s.", + __PRETTY_FUNCTION__, + type_to_string(tmpctx, struct amount_msat, &h->htlc_total), + i, + type_to_string(tmpctx, struct amount_msat, &pf->amounts[i])); + } + if (h->num_htlcs == 0) + { + plugin_err(pay_plugin->plugin, + "%s could not decrease HTLC count.", + __PRETTY_FUNCTION__); + } + h->num_htlcs--; + } +} + +static void commit_htlc_payflow( + struct chan_extra_map *chan_extra_map, + const struct pay_flow *pf) +{ + for (size_t i = 0; i < tal_count(pf->path_scids); i++) { + struct chan_extra_half *h = get_chan_extra_half_by_scid( + chan_extra_map, + pf->path_scids[i], + pf->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, pf->amounts[i])) + { + plugin_err(pay_plugin->plugin, + "%s could not add HTLC amounts, " + "pf->amounts[%lld] = %s.", + __PRETTY_FUNCTION__, + i, + type_to_string(tmpctx, struct amount_msat, &pf->amounts[i])); + } + h->num_htlcs++; + } +} + /* 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, @@ -137,11 +201,10 @@ static u64 flow_delay(const struct flow *flow) /* 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 payment *p, 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 */ @@ -199,29 +262,31 @@ static u32 *shadow_additions(const tal_t *ctx, 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) +static void destroy_payment_flow(struct pay_flow *pf) { - struct pay_flow **pay_flows - = tal_arr(payment, struct pay_flow *, tal_count(flows)); + list_del_from(&pf->payment->flows, &pf->list); +} +/* Calculates delays and converts to scids, and links to the payment. + * Frees flows. */ +static void convert_and_attach_flows(struct payment *payment, + struct gossmap *gossmap, + struct flow **flows STEALS, + const u32 *final_cltvs, + u64 *next_partid) +{ 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); + struct pay_flow *pf = tal(payment, struct pay_flow); size_t plen; plen = tal_count(f->path); - pay_flows[i] = pf; pf->payment = payment; + pf->state = PAY_FLOW_NOT_STARTED; pf->key.partid = (*next_partid)++; pf->key.groupid = payment->groupid; - pf->key.payment_hash = &payment->payment_hash; + 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); @@ -244,11 +309,25 @@ static struct pay_flow **flows_to_pay_flows(struct payment *payment, 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); + + /* Payment keeps a list of its flows. */ + list_add(&payment->flows, &pf->list); + + /* Increase totals for payment */ + amount_msat_accumulate(&payment->total_sent, pf->amounts[0]); + amount_msat_accumulate(&payment->total_delivering, + payflow_delivered(pf)); + + /* We keep a global map to identify notifications + * about this flow. */ + payflow_map_add(pay_plugin->payflow_map, pf); + + /* record these HTLC along the flow path */ + commit_htlc_payflow(pay_plugin->chan_extra_map, pf); + + tal_add_destructor(pf, destroy_payment_flow); } tal_free(flows); - - return pay_flows; } static bitmap *make_disabled_bitmap(const tal_t *ctx, @@ -280,12 +359,11 @@ static u64 flows_worst_delay(struct flow **flows) } /* FIXME: If only path has channels marked disabled, we should try... */ -static bool disable_htlc_violations_oneflow(struct renepay * renepay, +static bool disable_htlc_violations_oneflow(struct payment *p, 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++) { @@ -308,7 +386,7 @@ static bool disable_htlc_violations_oneflow(struct renepay * renepay, reason); /* Add this for future searches for this payment. */ - tal_arr_expand(&renepay->disabled, scid); + tal_arr_expand(&p->disabled, scid); /* Add to existing bitmap */ bitmap_set_bit(disabled, gossmap_chan_idx(gossmap, flow->path[i])); @@ -319,7 +397,7 @@ static bool disable_htlc_violations_oneflow(struct renepay * renepay, /* 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, +static bool disable_htlc_violations(struct payment *payment, struct flow **flows, const struct gossmap *gossmap, bitmap *disabled) @@ -328,40 +406,35 @@ static bool disable_htlc_violations(struct renepay *renepay, /* 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], + disabled_some |= disable_htlc_violations_oneflow(payment, 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, - const char **err_msg) +const char *add_payflows(const tal_t *ctx, + struct payment *p, + struct amount_msat amount, + struct amount_msat feebudget, + bool is_entire_payment, + enum jsonrpc_errcode *ecode) { - *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); + disabled = make_disabled_bitmap(tmpctx, pay_plugin->gossmap, p->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; + *ecode = PAY_ROUTE_NOT_FOUND; + return tal_fmt(ctx, "We don't have any channels."); } 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; + *ecode = PAY_ROUTE_NOT_FOUND; + return tal_fmt(ctx, "Destination is unknown in the network gossip."); } for (;;) { @@ -369,7 +442,7 @@ struct pay_flow **get_payflows(struct renepay * renepay, double prob; struct amount_msat fee; u64 delay; - bool too_unlikely, too_expensive, too_delayed; + bool too_expensive, too_delayed; const u32 *final_cltvs; flows = minflow(tmpctx, pay_plugin->gossmap, src, dst, @@ -385,10 +458,10 @@ struct pay_flow **get_payflows(struct renepay * renepay, "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; + *ecode = PAY_ROUTE_NOT_FOUND; + return tal_fmt(ctx, + "minflow couldn't find a feasible flow for %s", + type_to_string(tmpctx,struct amount_msat,&amount)); } /* Are we unhappy? */ @@ -403,29 +476,18 @@ struct pay_flow **get_payflows(struct renepay * renepay, type_to_string(tmpctx,struct amount_msat,&fee), delay); - 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; + *ecode = PAY_ROUTE_TOO_EXPENSIVE; + return tal_fmt(ctx, + "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)); } too_delayed = (delay > p->maxdelay); if (too_delayed) { @@ -435,11 +497,11 @@ struct pay_flow **get_payflows(struct renepay * renepay, /* 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; + *ecode = PAY_ROUTE_TOO_EXPENSIVE; + return tal_fmt(ctx, + "CLTV delay exceeds our CLTV budget, " + "delay = %"PRIu64" (maxdelay = %u)", + delay, p->maxdelay); } p->delay_feefactor *= 2; @@ -454,7 +516,7 @@ struct pay_flow **get_payflows(struct renepay * renepay, * 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, + if (disable_htlc_violations(p, flows, pay_plugin->gossmap, disabled)) { continue; // retry @@ -463,19 +525,16 @@ struct pay_flow **get_payflows(struct renepay * renepay, /* 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); + p, 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; + * pay_flows in the current payment, to outlive the + * current gossmap. */ + convert_and_attach_flows(p, pay_plugin->gossmap, + flows, final_cltvs, + &p->next_partid); + return NULL; } - - return pay_flows; - -fail: - return NULL; } const char *flow_path_to_str(const tal_t *ctx, const struct pay_flow *flow) @@ -554,87 +613,76 @@ const char* fmt_payflows(const tal_t *ctx, return json_out_contents(jout,&len); } -void remove_htlc_payflow( - struct chan_extra_map *chan_extra_map, - struct pay_flow *flow) +/* How much does this flow deliver to destination? */ +struct amount_msat payflow_delivered(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_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--; - } + return flow->amounts[tal_count(flow->amounts)-1]; } -void commit_htlc_payflow( - struct chan_extra_map *chan_extra_map, - const struct pay_flow *flow) + +static struct pf_result *pf_resolve(struct pay_flow *pf, + enum pay_flow_state oldstate, + enum pay_flow_state newstate, + bool reconsider) { - 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++; + assert(pf->state == oldstate); + pf->state = newstate; + + /* If it didn't deliver, remove from totals */ + if (pf->state != PAY_FLOW_SUCCESS) { + amount_msat_reduce(&pf->payment->total_delivering, + payflow_delivered(pf)); + amount_msat_reduce(&pf->payment->total_sent, pf->amounts[0]); } + + /* Subtract HTLC counters from the path */ + remove_htlc_payflow(pay_plugin->chan_extra_map, pf); + /* And remove from the global map: no more notifications about this! */ + payflow_map_del(pay_plugin->payflow_map, pf); + + if (reconsider) + payment_reconsider(pf->payment); + return NULL; } -/* How much does this flow deliver to destination? */ -struct amount_msat payflow_delivered(const struct pay_flow *flow) +/* We've been notified that a pay_flow has failed */ +struct pf_result *pay_flow_failed(struct pay_flow *pf) { - return flow->amounts[tal_count(flow->amounts)-1]; + return pf_resolve(pf, PAY_FLOW_IN_PROGRESS, PAY_FLOW_FAILED, true); } -struct pay_flow* payflow_fail(struct pay_flow *flow) +/* We've been notified that a pay_flow has failed, payment is done. */ +struct pf_result *pay_flow_failed_final(struct pay_flow *pf, + enum jsonrpc_errcode final_error, + const char *final_msg TAKES) { - debug_assert(flow); - struct payment * p = flow->payment; - debug_assert(p); + pf->final_error = final_error; + pf->final_msg = tal_strdup(pf, final_msg); - payment_fail(p); - amount_msat_reduce(&p->total_delivering, payflow_delivered(flow)); - amount_msat_reduce(&p->total_sent, flow->amounts[0]); + return pf_resolve(pf, PAY_FLOW_IN_PROGRESS, PAY_FLOW_FAILED_FINAL, true); +} - /* Release the HTLCs in the uncertainty_network. */ - return tal_free(flow); +/* We've been notified that a pay_flow has failed, adding gossip. */ +struct pf_result *pay_flow_failed_adding_gossip(struct pay_flow *pf) +{ + /* Don't bother reconsidering until addgossip done */ + return pf_resolve(pf, PAY_FLOW_IN_PROGRESS, PAY_FLOW_FAILED_GOSSIP_PENDING, + false); } +/* We've finished adding gossip. */ +struct pf_result *pay_flow_finished_adding_gossip(struct pay_flow *pf) +{ + assert(pf->state == PAY_FLOW_FAILED_GOSSIP_PENDING); + pf->state = PAY_FLOW_FAILED; + payment_reconsider(pf->payment); + return NULL; +} + +/* We've been notified that a pay_flow has succeeded. */ +struct pf_result *pay_flow_succeeded(struct pay_flow *pf, + const struct preimage *preimage) +{ + pf->payment_preimage = tal_dup(pf, struct preimage, preimage); + return pf_resolve(pf, PAY_FLOW_IN_PROGRESS, PAY_FLOW_SUCCESS, true); +} diff --git a/plugins/renepay/pay_flow.h b/plugins/renepay/pay_flow.h index e519e9f7f098..4756ad7a2a4e 100644 --- a/plugins/renepay/pay_flow.h +++ b/plugins/renepay/pay_flow.h @@ -8,20 +8,43 @@ #include #include +/* There are several states a payment can be in */ +enum pay_flow_state { + /* Created, but not sent to sendpay */ + PAY_FLOW_NOT_STARTED, + /* Normally, here */ + PAY_FLOW_IN_PROGRESS, + /* Failed: we've fed the data back to the uncertainly network. */ + PAY_FLOW_FAILED, + /* Failed from the final node, so give up: see ->final_error. */ + PAY_FLOW_FAILED_FINAL, + /* Failed, but still updating gossip. */ + PAY_FLOW_FAILED_GOSSIP_PENDING, + /* Succeeded: see ->payment_preimage. */ + PAY_FLOW_SUCCESS, +}; +#define NUM_PAY_FLOW (PAY_FLOW_SUCCESS + 1) + /* This is like a struct flow, but independent of gossmap, and contains * all we need to actually send the part payment. */ struct pay_flow { + /* Linked from payment->flows */ + struct list_node list; + + enum pay_flow_state state; + /* Iff state == PAY_FLOW_SUCCESS */ + const struct preimage *payment_preimage; + /* Iff state == PAY_FAILED_FINAL */ + enum jsonrpc_errcode final_error; + const char *final_msg; + /* 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; + struct sha256 payment_hash; u64 groupid; u64 partid; } key; @@ -39,9 +62,9 @@ struct pay_flow { }; static inline struct payflow_key -payflow_key(struct sha256 *hash, u64 groupid, u64 partid) +payflow_key(const struct sha256 *hash, u64 groupid, u64 partid) { - struct payflow_key k= {hash,groupid,partid}; + struct payflow_key k= {*hash,groupid,partid}; return k; } @@ -53,49 +76,62 @@ static inline const char* fmt_payflow_key( ctx, "key: groupid=%"PRIu64", partid=%"PRIu64", payment_hash=%s", k->groupid,k->partid, - type_to_string(ctx,struct sha256,k->payment_hash)); + type_to_string(ctx,struct sha256,&k->payment_hash)); return str; } -static inline const struct payflow_key +static inline const struct payflow_key * payflow_get_key(const struct pay_flow * pf) { - return pf->key; + return &pf->key; } -static inline size_t payflow_key_hash(const struct payflow_key k) +static inline size_t payflow_key_hash(const struct payflow_key *k) { - return k.payment_hash->u.u32[0] ^ (k.groupid << 32) ^ k.partid; + return k->payment_hash.u.u32[0] ^ (k->groupid << 32) ^ k->partid; } static inline bool payflow_key_equal(const struct pay_flow *pf, - const struct payflow_key k) + 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); + 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, - const char **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); - +/* Add one or more IN_PROGRESS pay_flow to payment. Return NULL if we did, + * otherwise an error message (and sets *ecode). */ +const char *add_payflows(const tal_t *ctx, + struct payment *payment, + struct amount_msat amount, + struct amount_msat feebudget, + bool is_entire_payment, + enum jsonrpc_errcode *ecode); + +/* Each payflow is eventually terminated by one of these. + * + * To make sure you deal with flows, they return a special type. + */ + +/* We've been notified that a pay_flow has failed */ +struct pf_result *pay_flow_failed(struct pay_flow *pf STEALS); +/* We've been notified that a pay_flow has failed, payment is done. */ +struct pf_result *pay_flow_failed_final(struct pay_flow *pf STEALS, + enum jsonrpc_errcode final_error, + const char *final_msg TAKES); +/* We've been notified that a pay_flow has failed, adding gossip. */ +struct pf_result *pay_flow_failed_adding_gossip(struct pay_flow *pf STEALS); +/* We've finished adding gossip. */ +struct pf_result *pay_flow_finished_adding_gossip(struct pay_flow *pf STEALS); +/* We've been notified that a pay_flow has succeeded. */ +struct pf_result *pay_flow_succeeded(struct pay_flow *pf STEALS, + const struct preimage *preimage); + +/* Formatting helpers */ const char *flow_path_to_str(const tal_t *ctx, const struct pay_flow *flow); const char* fmt_payflows(const tal_t *ctx, @@ -104,9 +140,4 @@ const char* fmt_payflows(const tal_t *ctx, /* 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 index deff954957d0..da40162847b0 100644 --- a/plugins/renepay/payment.c +++ b/plugins/renepay/payment.c @@ -1,76 +1,75 @@ #include "config.h" #include +#include #include +#include #include -struct payment * payment_new(struct renepay * renepay) +struct payment *payment_new(const tal_t *ctx, + struct command *cmd, + const char *invstr TAKES, + const char *label TAKES, + const char *description TAKES, + const struct sha256 *local_offer_id TAKES, + const struct secret *payment_secret TAKES, + const u8 *payment_metadata TAKES, + const struct node_id *destination, + const struct sha256 *payment_hash, + struct amount_msat amount, + struct amount_msat maxfee, + unsigned int maxdelay, + u64 retryfor, + u16 final_cltv, + /* Tweakable in DEVELOPER mode */ + u64 base_fee_penalty, + u64 prob_cost_factor, + u64 riskfactor_millionths, + u64 min_prob_success_millionths, + bool use_shadow) { - struct payment *p = tal(renepay,struct payment); - p->renepay = renepay; + struct payment *p = tal(ctx,struct payment); + p->cmd = cmd; p->paynotes = tal_arr(p, const char *, 0); p->total_sent = AMOUNT_MSAT(0); p->total_delivering = AMOUNT_MSAT(0); - p->invstr=NULL; + p->invstr = tal_strdup(p, invstr); - 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->amount = amount; + p->destination = *destination; + p->payment_hash = *payment_hash; + if (!amount_msat_add(&p->maxspend, amount, maxfee)) + p->maxspend = AMOUNT_MSAT(UINT64_MAX); + + p->maxdelay = maxdelay; + p->start_time = time_now(); + p->stop_time = timeabs_add(p->start_time, time_from_sec(retryfor)); p->preimage = NULL; - p->payment_secret=NULL; - p->payment_metadata=NULL; + p->payment_secret = tal_dup_or_null(p, struct secret, payment_secret); + p->payment_metadata = tal_dup_talarr(p, u8, payment_metadata); p->status=PAYMENT_PENDING; - p->final_cltv=0; + list_head_init(&p->flows); + p->final_cltv=final_cltv; // p->list= - p->description=NULL; - p->label=NULL; + p->description = tal_strdup_or_null(p, description); + p->label = tal_strdup_or_null(p, label); - p->delay_feefactor=0; - p->base_fee_penalty=0; - p->prob_cost_factor=0; - p->min_prob_success=0; + p->delay_feefactor = riskfactor_millionths / 1e6; + p->base_fee_penalty = base_fee_penalty; + p->prob_cost_factor = prob_cost_factor; + p->min_prob_success = min_prob_success_millionths / 1e6; - p->local_offer_id=NULL; - p->use_shadow=true; + p->local_offer_id = tal_dup_or_null(p, struct sha256, local_offer_id); + p->use_shadow = use_shadow; 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; -} - + p->local_gossmods = gossmap_localmods_new(p); + p->disabled = tal_arr(p,struct short_channel_id,0); + p->next_partid=1; + p->progress_deadline = NULL; -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; + return p; } struct amount_msat payment_sent(const struct payment *p) @@ -109,6 +108,9 @@ void payment_note(struct payment *p, const char *fmt, ...) va_end(ap); tal_arr_expand(&p->paynotes, str); debug_info("%s",str); + + if (p->cmd) + plugin_notify_message(p->cmd, LOG_INFORM, "%s", str); } void payment_assert_delivering_incomplete(const struct payment *p) @@ -132,36 +134,20 @@ void payment_assert_delivering_all(const struct payment *p) } } - -int renepay_current_attempt(const struct renepay * renepay) -{ - return renepay->next_attempt-1; -} -int renepay_attempt_count(const struct renepay * renepay) +static struct command_result *payment_success(struct payment *p) { - 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); + /* We only finish command once: its destructor clears this. */ + if (!p->cmd) + return NULL; struct json_stream *response - = jsonrpc_stream_success(renepay->cmd); + = jsonrpc_stream_success(p->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_u32(response, "parts", payment_parts(p)); json_add_amount_msat(response, "amount_msat", p->amount); json_add_amount_msat(response, "amount_sent_msat", @@ -169,62 +155,232 @@ struct command_result *renepay_success(struct renepay * renepay) json_add_string(response, "status", "complete"); json_add_node_id(response, "destination", &p->destination); - return command_finished(renepay->cmd, response); + return command_finished(p->cmd, response); } -struct command_result *renepay_fail( - struct renepay * renepay, +struct command_result *payment_fail( + struct payment *payment, 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); + /* We usually get called because a flow failed, but we + * can also get called because we couldn't route any more + * or some strange error. */ + payment->status = PAYMENT_FAIL; + + /* We only finish command once: its destructor clears this. */ + if (!payment->cmd) + return NULL; va_list args; va_start(args, fmt); char *message = tal_vfmt(tmpctx,fmt,args); va_end(args); - debug_paynote(renepay->payment,"%s",message); + debug_paynote(payment,"%s",message); - return command_fail(renepay->cmd,code,"%s",message); + return command_fail(payment->cmd,code,"%s",message); } -u64 renepay_parts(const struct renepay *renepay) +u64 payment_parts(const struct payment *payment) { - return renepay->next_partid-1; + return payment->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) +void payment_reconsider(struct payment *payment) { - 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; + struct pay_flow *i, *next; + bool have_state[NUM_PAY_FLOW] = {false}; + enum jsonrpc_errcode final_error, ecode; + const char *final_msg; + const char *errmsg; + + plugin_log(pay_plugin->plugin, LOG_DBG, "payment_reconsider"); + + /* Harvest results and free up finished flows */ + list_for_each_safe(&payment->flows, i, next, list) { + plugin_log(pay_plugin->plugin, LOG_DBG, "Flow in state %u", i->state); + have_state[i->state] = true; + + switch (i->state) { + case PAY_FLOW_NOT_STARTED: + /* Can't happen: we start just after we add. */ + plugin_err(pay_plugin->plugin, "flow not started?"); + case PAY_FLOW_IN_PROGRESS: + /* Don't free, it's still going! */ + continue; + case PAY_FLOW_FAILED: + break; + case PAY_FLOW_FAILED_FINAL: + final_error = i->final_error; + final_msg = tal_steal(tmpctx, i->final_msg); + break; + case PAY_FLOW_FAILED_GOSSIP_PENDING: + break; + case PAY_FLOW_SUCCESS: + if (payment->preimage) { + /* This should be impossible without breaking SHA256 */ + if (!preimage_eq(payment->preimage, + i->payment_preimage)) { + plugin_err(pay_plugin->plugin, + "Impossible preimage clash for %s: %s and %s?", + type_to_string(tmpctx, + struct sha256, + &payment->payment_hash), + type_to_string(tmpctx, + struct preimage, + payment->preimage), + type_to_string(tmpctx, + struct preimage, + i->payment_preimage)); + } + } else { + payment->preimage = tal_dup(payment, struct preimage, + i->payment_preimage); + } + break; + } + tal_free(i); + } + + /* First, did one of these succeed? */ + if (have_state[PAY_FLOW_SUCCESS]) { + plugin_log(pay_plugin->plugin, LOG_DBG, "one succeeded!"); + + switch (payment->status) { + case PAYMENT_PENDING: + /* The normal case: one part succeeded, we can succeed immediately */ + payment_success(payment); + payment->status = PAYMENT_SUCCESS; + /* fall thru */ + case PAYMENT_SUCCESS: + /* Since we already succeeded, cmd must be NULL */ + assert(payment->cmd == NULL); + break; + case PAYMENT_FAIL: + /* OK, they told us it failed, but also + * succeeded? It's theoretically possible, + * but someone screwed up. */ + plugin_log(pay_plugin->plugin, LOG_BROKEN, + "Destination %s succeeded payment %s" + " (preimage %s) after previous final failure?", + type_to_string(tmpctx, struct node_id, + &payment->destination), + type_to_string(tmpctx, struct sha256, + &payment->payment_hash), + type_to_string(tmpctx, + struct preimage, + payment->preimage)); + break; + } + + /* We don't need to do anything else. */ + return; + } + + /* One of these returned an error from the destination? */ + if (have_state[PAY_FLOW_FAILED_FINAL]) { + plugin_log(pay_plugin->plugin, LOG_DBG, "one failed final!"); + switch (payment->status) { + case PAYMENT_PENDING: + /* The normal case: we can fail immediately */ + payment_fail(payment, final_error, "%s", final_msg); + /* fall thru */ + case PAYMENT_FAIL: + /* Since we already failed, cmd must be NULL */ + assert(payment->cmd == NULL); + break; + case PAYMENT_SUCCESS: + /* OK, they told us it failed, but also + * succeeded? It's theoretically possible, + * but someone screwed up. */ + plugin_log(pay_plugin->plugin, LOG_BROKEN, + "Destination %s failed payment %s with %u/%s" + " after previous success?", + type_to_string(tmpctx, struct node_id, + &payment->destination), + type_to_string(tmpctx, struct sha256, + &payment->payment_hash), + final_error, final_msg); + break; + } + + /* We don't need to do anything else. */ + return; + } + + /* Now, do we still care about retrying the payment? It could + * have terminated a while ago, and we're just collecting + * outstanding results. */ + switch (payment->status) { + case PAYMENT_PENDING: + break; + case PAYMENT_FAIL: + case PAYMENT_SUCCESS: + assert(!payment->cmd); + plugin_log(pay_plugin->plugin, LOG_DBG, "payment already status %u!", + payment->status); + return; + } + + /* Are we waiting on addgossip? We'll come back later when + * they call pay_flow_finished_adding_gossip. */ + if (have_state[PAY_FLOW_FAILED_GOSSIP_PENDING]) { + plugin_log(pay_plugin->plugin, LOG_DBG, + "%s waiting on addgossip return", + type_to_string(tmpctx, struct sha256, + &payment->payment_hash)); + return; + } + + /* Do we still have pending payment parts? First time, we set + * up a deadline so we don't respond immediately to every + * return: it's better to gather a few failed flows before + * retrying. */ + if (have_state[PAY_FLOW_IN_PROGRESS]) { + struct timemono now = time_mono(); + + /* If we don't have a deadline yet, set it now. */ + if (!payment->progress_deadline) { + payment->progress_deadline = tal(payment, struct timemono); + *payment->progress_deadline = timemono_add(now, + time_from_msec(TIMER_COLLECT_FAILURES_MSEC)); + plugin_log(pay_plugin->plugin, LOG_DBG, "Set deadline"); + } + + /* FIXME: add timemono_before to ccan/time */ + if (time_less_(now.ts, payment->progress_deadline->ts)) { + /* Come back later. */ + /* We don't care that this temporily looks like a leak; we don't even + * care if we end up with multiple outstanding. They just check + * the progress_deadline. */ + plugin_log(pay_plugin->plugin, LOG_DBG, "Setting timer to kick us"); + notleak(plugin_timer(pay_plugin->plugin, + timemono_between(*payment->progress_deadline, now), + payment_reconsider, payment)); + return; + } + } + + /* At this point, we may have some funds to deliver (or we + * could still be waiting). */ + if (amount_msat_greater_eq(payment->total_delivering, payment->amount)) { + plugin_log(pay_plugin->plugin, LOG_DBG, "No more to deliver right now"); + assert(have_state[PAY_FLOW_IN_PROGRESS]); + return; + } + + /* If we had a deadline, reset it */ + payment->progress_deadline = tal_free(payment->progress_deadline); + + /* Before we do that, make sure we're not going over time. */ + if (time_after(time_now(), payment->stop_time)) { + payment_fail(payment, PAY_STOPPED_RETRYING, "Timed out"); + return; + } + + plugin_log(pay_plugin->plugin, LOG_DBG, "Retrying payment"); + errmsg = try_paying(tmpctx, payment, &ecode); + if (errmsg) + payment_fail(payment, ecode, "%s", errmsg); } diff --git a/plugins/renepay/payment.h b/plugins/renepay/payment.h index 96da7a9ce2f5..219b4a2f534a 100644 --- a/plugins/renepay/payment.h +++ b/plugins/renepay/payment.h @@ -8,9 +8,30 @@ enum payment_status { PAYMENT_PENDING, PAYMENT_SUCCESS, PAYMENT_FAIL }; - struct payment { - struct renepay * renepay; + /* Inside pay_plugin->payments list */ + struct list_node list; + + /* Overall, how are we going? */ + enum payment_status status; + + /* The flows we are managing. */ + struct list_head flows; + + /* Deadline for flow status collection. */ + struct timemono *progress_deadline; + + /* The command if still running */ + struct command *cmd; + + /* Localmods to apply to gossip_map for our own use. */ + struct gossmap_localmods *local_gossmods; + + /* Channels we decided to disable for various reasons. */ + struct short_channel_id *disabled; + + /* Used in get_payflows to set ids to each pay_flow. */ + u64 next_partid; /* Chatty description of attempts. */ const char **paynotes; @@ -29,7 +50,6 @@ struct payment { struct node_id destination; struct sha256 payment_hash; - /* Limits on what routes we'll accept. */ struct amount_msat maxspend; @@ -51,14 +71,8 @@ struct payment { /* 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; @@ -96,47 +110,31 @@ struct payment { /* 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); +struct payment *payment_new(const tal_t *ctx, + struct command *cmd, + const char *invstr TAKES, + const char *label TAKES, + const char *description TAKES, + const struct sha256 *local_offer_id TAKES, + const struct secret *payment_secret TAKES, + const u8 *payment_metadata TAKES, + const struct node_id *destination, + const struct sha256 *payment_hash, + struct amount_msat amount, + struct amount_msat maxfee, + unsigned int maxdelay, + u64 retryfor, + u16 final_cltv, + /* Tweakable in DEVELOPER mode */ + u64 base_fee_penalty, + u64 prob_cost_factor, + u64 riskfactor_millionths, + u64 min_prob_success_millionths, + bool use_shadow); -void payment_fail(struct payment * p); -void payment_success(struct payment * p); struct amount_msat payment_sent(const struct payment *p); struct amount_msat payment_delivered(const struct payment *p); struct amount_msat payment_amount(const struct payment *p); @@ -146,18 +144,14 @@ void payment_note(struct payment *p, const char *fmt, ...); void payment_assert_delivering_incomplete(const struct payment *p); void payment_assert_delivering_all(const struct payment *p); +/* A flow has changed state, or we've hit a timeout: do something! */ +void payment_reconsider(struct payment *p); -int renepay_current_attempt(const struct renepay *renepay); -int renepay_attempt_count(const struct renepay *renepay); -void renepay_new_attempt(struct renepay *renepay); +u64 payment_parts(const struct payment *payment); -struct command_result *renepay_success(struct renepay *renepay); - -struct command_result *renepay_fail( - struct renepay * renepay, +struct command_result *payment_fail( + struct payment *payment, enum jsonrpc_errcode code, const char *fmt, ...); -u64 renepay_parts(const struct renepay *renepay); - #endif /* LIGHTNING_PLUGINS_RENEPAY_PAYMENT_H */ diff --git a/plugins/renepay/test/run-arc.c b/plugins/renepay/test/run-arc.c index 1c1304171935..293520bcdbdc 100644 --- a/plugins/renepay/test/run-arc.c +++ b/plugins/renepay/test/run-arc.c @@ -11,9 +11,6 @@ #include "../mcf.c" -/* update-mocks isn't quiet smart enough for this, so place here */ -struct pay_plugin *const pay_plugin; - /* AUTOGENERATED MOCKS START */ /* Generated stub for flow_complete */ void flow_complete(struct flow *flow UNNEEDED, @@ -43,6 +40,8 @@ s64 linear_fee_cost( double base_fee_penalty UNNEEDED, double delay_feefactor UNNEEDED) { fprintf(stderr, "linear_fee_cost called!\n"); abort(); } +/* Generated stub for pay_plugin */ +struct pay_plugin *pay_plugin; /* AUTOGENERATED MOCKS END */ int main(int argc, char *argv[]) diff --git a/plugins/renepay/test/run-mcf-diamond.c b/plugins/renepay/test/run-mcf-diamond.c index b764d5b0eb91..6436e01c6281 100644 --- a/plugins/renepay/test/run-mcf-diamond.c +++ b/plugins/renepay/test/run-mcf-diamond.c @@ -17,6 +17,16 @@ #include #include +/* AUTOGENERATED MOCKS START */ +/* Generated stub for pay_plugin */ +struct pay_plugin *pay_plugin; +/* Generated stub for try_paying */ +const char *try_paying(const tal_t *ctx UNNEEDED, + struct payment *payment UNNEEDED, + enum jsonrpc_errcode *ecode UNNEEDED) +{ fprintf(stderr, "try_paying called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + static u8 empty_map[] = { 0 }; diff --git a/plugins/renepay/test/run-mcf.c b/plugins/renepay/test/run-mcf.c index 723d53d60547..9d862f203716 100644 --- a/plugins/renepay/test/run-mcf.c +++ b/plugins/renepay/test/run-mcf.c @@ -17,6 +17,16 @@ #include #include +/* AUTOGENERATED MOCKS START */ +/* Generated stub for pay_plugin */ +struct pay_plugin *pay_plugin; +/* Generated stub for try_paying */ +const char *try_paying(const tal_t *ctx UNNEEDED, + struct payment *payment UNNEEDED, + enum jsonrpc_errcode *ecode UNNEEDED) +{ fprintf(stderr, "try_paying called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + static void swap(int *a, int *b) { int temp = *a; diff --git a/plugins/renepay/test/run-payflow_map.c b/plugins/renepay/test/run-payflow_map.c index 9627ef810ecc..1d3c65261133 100644 --- a/plugins/renepay/test/run-payflow_map.c +++ b/plugins/renepay/test/run-payflow_map.c @@ -31,14 +31,14 @@ static void destroy_payflow( } static struct pay_flow* new_payflow( const tal_t *ctx, - struct sha256 * payment_hash, + const 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.payment_hash = *payment_hash; p->key.groupid = gid; p->key.partid = pid; @@ -61,6 +61,7 @@ static void valgrind_ok1(void) { tal_t *local_ctx = tal(this_ctx,tal_t); + struct payflow_key key; struct pay_flow *p1 = new_payflow(local_ctx, &hash,1,1); @@ -69,17 +70,19 @@ static void valgrind_ok1(void) 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 = %zu\n",payflow_key_hash(p1->key)); - printf("key hash 2 = %zu\n",payflow_key_hash(p2->key)); + printf("key hash 1 = %zu\n",payflow_key_hash(&p1->key)); + printf("key hash 2 = %zu\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)); + key = payflow_key(&hash,1,1); + struct pay_flow *q1 = payflow_map_get(map, &key); + key = payflow_key(&hash,2,3); + struct pay_flow *q2 = payflow_map_get(map, &key); - assert(payflow_key_hash(q1->key)==payflow_key_hash(p1->key)); - assert(payflow_key_hash(q2->key)==payflow_key_hash(p2->key)); + 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); } diff --git a/plugins/renepay/test/run-testflow.c b/plugins/renepay/test/run-testflow.c index 711d5db78bbd..8b5111b255dc 100644 --- a/plugins/renepay/test/run-testflow.c +++ b/plugins/renepay/test/run-testflow.c @@ -16,6 +16,16 @@ #include "../uncertainty_network.c" #include "../mcf.c" +/* AUTOGENERATED MOCKS START */ +/* Generated stub for pay_plugin */ +struct pay_plugin *pay_plugin; +/* Generated stub for try_paying */ +const char *try_paying(const tal_t *ctx UNNEEDED, + struct payment *payment UNNEEDED, + enum jsonrpc_errcode *ecode UNNEEDED) +{ fprintf(stderr, "try_paying called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + static const u8 canned_map[] = { 0x0c, 0x80, 0x00, 0x01, 0xbc, 0x86, 0xe4, 0xbf, 0x95, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x40, 0x01, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/plugins/renepay/uncertainty_network.c b/plugins/renepay/uncertainty_network.c index 410e8561d8fd..4821de47eb31 100644 --- a/plugins/renepay/uncertainty_network.c +++ b/plugins/renepay/uncertainty_network.c @@ -41,7 +41,7 @@ bool uncertainty_network_check_invariants(struct chan_extra_map *chan_extra_map) static void add_hintchan( struct chan_extra_map *chan_extra_map, - struct renepay * renepay, + struct gossmap_localmods *local_gossmods, const struct node_id *src, const struct node_id *dst, u16 cltv_expiry_delta, @@ -63,9 +63,9 @@ static void add_hintchan( scid, MAX_CAP); /* FIXME: features? */ - gossmap_local_addchan(renepay->local_gossmods, + gossmap_local_addchan(local_gossmods, src, dst, &scid, NULL); - gossmap_local_updatechan(renepay->local_gossmods, + gossmap_local_updatechan(local_gossmods, &scid, /* We assume any HTLC is allowed */ AMOUNT_MSAT(0), MAX_CAP, @@ -84,27 +84,18 @@ static void add_hintchan( /* Add routehints provided by bolt11 */ void uncertainty_network_add_routehints( struct chan_extra_map *chan_extra_map, - struct renepay *renepay) + const struct route_info **routes, + struct payment *p) { - const struct payment *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++) { + for (size_t i = 0; i < tal_count(routes); i++) { /* Each one, presumably, leads to the destination */ - const struct route_info *r = b11->routes[i]; + const struct route_info *r = 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, + p->local_gossmods, + &r[j].pubkey, end, r[j].cltv_expiry_delta, r[j].short_channel_id, r[j].fee_base_msat, @@ -195,34 +186,34 @@ void uncertainty_network_update( void uncertainty_network_flow_success( struct chan_extra_map *chan_extra_map, - struct pay_flow *flow) + struct pay_flow *pf) { - for (size_t i = 0; i < tal_count(flow->path_scids); i++) + for (size_t i = 0; i < tal_count(pf->path_scids); i++) { chan_extra_sent_success( chan_extra_map, - flow->path_scids[i], - flow->path_dirs[i], - flow->amounts[i]); + pf->path_scids[i], + pf->path_dirs[i], + pf->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, + struct pay_flow *pf, 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], + pf->path_scids[i], + pf->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]. */ + * we would have to put here pf->amounts[i]. */ AMOUNT_MSAT(0)); } } @@ -232,11 +223,10 @@ void uncertainty_network_channel_can_send( bool uncertainty_network_update_from_listpeerchannels( struct chan_extra_map * chan_extra_map, struct node_id my_id, - struct renepay * renepay, + struct payment *p, const char *buf, const jsmntok_t *toks) { - struct payment * const p = renepay->payment; const jsmntok_t *channels, *channel; size_t i; @@ -270,7 +260,7 @@ bool uncertainty_network_update_from_listpeerchannels( type_to_string(tmpctx, struct short_channel_id, &scid)); - tal_arr_expand(&renepay->disabled, scid); + tal_arr_expand(&p->disabled, scid); continue; } @@ -302,7 +292,7 @@ bool uncertainty_network_update_from_listpeerchannels( /* Don't report opening/closing channels */ if (!json_tok_streq(buf, statetok, "CHANNELD_NORMAL")) { - tal_arr_expand(&renepay->disabled, scid); + tal_arr_expand(&p->disabled, scid); continue; } @@ -316,9 +306,9 @@ bool uncertainty_network_update_from_listpeerchannels( scid, capacity); /* FIXME: features? */ - gossmap_local_addchan(renepay->local_gossmods, + gossmap_local_addchan(p->local_gossmods, &src, &dst, &scid, NULL); - gossmap_local_updatechan(renepay->local_gossmods, + gossmap_local_updatechan(p->local_gossmods, &scid, /* TODO(eduardo): does it diff --git a/plugins/renepay/uncertainty_network.h b/plugins/renepay/uncertainty_network.h index 6a8a0bffc9f0..fda20e0249b1 100644 --- a/plugins/renepay/uncertainty_network.h +++ b/plugins/renepay/uncertainty_network.h @@ -14,7 +14,8 @@ 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); + const struct route_info **routes, + struct payment *p); /* Mirror the gossmap in the public uncertainty network. * result: Every channel in gossmap must have associated data in chan_extra_map, @@ -40,7 +41,7 @@ void uncertainty_network_channel_can_send( bool uncertainty_network_update_from_listpeerchannels( struct chan_extra_map * chan_extra_map, struct node_id my_id, - struct renepay * renepay, + struct payment *payment, const char *buf, const jsmntok_t *toks); diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index 94d33085273e..f78f0fa561ad 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -160,9 +160,6 @@ bool json_to_u32(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u32 /* Generated stub for json_to_u64 */ bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u64 *num UNNEEDED) { fprintf(stderr, "json_to_u64 called!\n"); abort(); } -/* Generated stub for json_to_s64 */ -bool json_to_s64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, s64 *num UNNEEDED) -{ fprintf(stderr, "json_to_s64 called!\n"); abort(); } /* Generated stub for json_tok_bin_from_hex */ u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) { fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } diff --git a/tests/test_renepay.py b/tests/test_renepay.py index 53c5afbe0f14..7029f213f3ee 100644 --- a/tests/test_renepay.py +++ b/tests/test_renepay.py @@ -1,10 +1,11 @@ from fixtures import * # noqa: F401,F403 from pyln.client import RpcError, Millisatoshi -from utils import only_one, wait_for, mine_funding_to_announce, sync_blockheight +from utils import only_one, wait_for, mine_funding_to_announce, sync_blockheight, TEST_NETWORK import pytest import random import time import json +import subprocess def test_simple(node_factory): @@ -48,6 +49,8 @@ def test_errors(node_factory, bitcoind): 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'] + inv_deleted = l6.rpc.invoice(send_amount, 'test_renepay2', 'description2')['bolt11'] + l6.rpc.delinvoice('test_renepay2', 'unpaid') failmsg = r'We don\'t have any channels' with pytest.raises(RpcError, match=failmsg): @@ -57,7 +60,7 @@ def test_errors(node_factory, bitcoind): node_factory.join_nodes([l1, l3, l5], wait_for_announce=True, fundamount=1000000) - failmsg = r'Destination is unreacheable in the network gossip.' + failmsg = r'Destination is unknown in the network gossip.' with pytest.raises(RpcError, match=failmsg): l1.rpc.call('renepay', {'invstring': inv}) @@ -81,6 +84,14 @@ def test_errors(node_factory, bitcoind): assert details['amount_msat'] == send_amount assert details['destination'] == l6.info['id'] + # Test error from final node. + with pytest.raises(RpcError) as err: + l1.rpc.call('renepay', {'invstring': inv_deleted}) + + PAY_DESTINATION_PERM_FAIL = 203 + assert err.value.error['code'] == PAY_DESTINATION_PERM_FAIL + assert 'WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS' in err.value.error['message'] + @pytest.mark.developer("needs to deactivate shadow routing") @pytest.mark.openchannel('v1') @@ -198,7 +209,7 @@ def test_limits(node_factory): # FIXME: pylightning should define these! # PAY_STOPPED_RETRYING = 210 - PAY_ROUTE_NOT_FOUND = 205 + PAY_ROUTE_TOO_EXPENSIVE = 206 inv = l6.rpc.invoice("any", "any", 'description') @@ -207,7 +218,7 @@ def test_limits(node_factory): 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 + assert err.value.error['code'] == PAY_ROUTE_TOO_EXPENSIVE # TODO(eduardo): which error code shall we use here? # TODO(eduardo): shall we list attempts in renepay? @@ -218,7 +229,7 @@ def test_limits(node_factory): 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 + assert err.value.error['code'] == PAY_ROUTE_TOO_EXPENSIVE inv2 = l6.rpc.invoice("800000sat", "inv2", 'description') l1.rpc.call( @@ -299,8 +310,19 @@ def test_hardmpp(node_factory): print(json.dumps(l3.rpc.listpeerchannels()), file=f) inv2 = l6.rpc.invoice("1800000sat", "inv2", 'description') - l1.rpc.call( - 'renepay', {'invstring': inv2['bolt11']}) + + out = subprocess.check_output(['cli/lightning-cli', + '--network={}'.format(TEST_NETWORK), + '--lightning-dir={}' + .format(l1.daemon.lightning_dir), + '-k', + 'renepay', 'invstring={}'.format(inv2['bolt11'])]).decode('utf-8') + lines = out.split('\n') + # First comes commentry + assert any([l.startswith('#') for l in lines]) + + # Now comes JSON + json.loads("".join([l for l in lines if not l.startswith('#')])) l1.wait_for_htlcs() invoice = only_one(l6.rpc.listinvoices('inv2')['invoices']) assert isinstance(invoice['amount_received_msat'], Millisatoshi) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 99c2cc057877..74f4c0b93d9e 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -309,6 +309,10 @@ void json_add_pubkey(struct json_stream *response UNNEEDED, void json_add_s32(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, int32_t value UNNEEDED) { fprintf(stderr, "json_add_s32 called!\n"); abort(); } +/* Generated stub for json_add_s64 */ +void json_add_s64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, + int64_t value UNNEEDED) +{ fprintf(stderr, "json_add_s64 called!\n"); abort(); } /* Generated stub for json_add_secret */ void json_add_secret(struct json_stream *response UNNEEDED, const char *fieldname UNNEEDED, @@ -354,10 +358,6 @@ void json_add_u32(struct json_stream *result UNNEEDED, const char *fieldname UNN void json_add_u64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, uint64_t value UNNEEDED) { fprintf(stderr, "json_add_u64 called!\n"); abort(); } -/* Generated stub for json_add_s64 */ -void json_add_s64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, - int64_t value UNNEEDED) -{ fprintf(stderr, "json_add_s64 called!\n"); abort(); } /* Generated stub for json_add_uncommitted_channel */ void json_add_uncommitted_channel(struct json_stream *response UNNEEDED, const struct uncommitted_channel *uc UNNEEDED,