diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index bfc73ddd5..c77d75d83 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -61,6 +61,7 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd, p->invstring = NULL; p->routetxt = NULL; p->max_htlcs = UINT32_MAX; + p->aborterror = NULL; /* Copy over the relevant pieces of information. */ if (parent != NULL) { @@ -1884,6 +1885,14 @@ static void payment_finished(struct payment *p) json_add_string(ret, "status", "complete"); + if (command_finished(cmd, ret)) {/* Ignore result. */} + return; + } else if (p->aborterror != NULL) { + /* We set an explicit toplevel error message, + * so let's report that. */ + ret = jsonrpc_stream_fail(cmd, PAY_STOPPED_RETRYING, + p->aborterror); + payment_json_add_attempts(ret, "attempts", p); if (command_finished(cmd, ret)) {/* Ignore result. */} return; } else if (result.failure == NULL || result.failure->failcode < NODE) { @@ -2028,6 +2037,32 @@ void payment_continue(struct payment *p) abort(); } +void payment_abort(struct payment *p, const char *fmt, ...) { + va_list ap; + struct payment *root = payment_root(p); + payment_set_step(p, PAYMENT_STEP_FAILED); + p->end_time = time_now(); + + va_start(ap, fmt); + p->failreason = tal_vfmt(p, fmt, ap); + va_end(ap); + + root->abort = true; + + /* Only set the abort error if it's not yet set, otherwise we + * might end up clobbering the earliest and decisive failure + * with less relevant ones. */ + if (root->aborterror == NULL) + root->aborterror = tal_dup_talarr(root, char, p->failreason); + + paymod_log(p, LOG_INFORM, "%s", p->failreason); + + /* Do not use payment_continue, because that'd continue + * applying the modifiers before calling + * payment_finished(). */ + payment_finished(p); +} + void payment_fail(struct payment *p, const char *fmt, ...) { va_list ap; diff --git a/plugins/libplugin-pay.h b/plugins/libplugin-pay.h index a25ae4c29..e1c355516 100644 --- a/plugins/libplugin-pay.h +++ b/plugins/libplugin-pay.h @@ -292,6 +292,10 @@ struct payment { * and payment_lower_max_htlcs functions. */ u32 max_htlcs; + + /* A human readable error message that is used as a top-level + * explanation if a payment is aborted. */ + char *aborterror; }; struct payment_modifier { @@ -436,6 +440,13 @@ void payment_set_step(struct payment *p, enum payment_step newstep); /* Fails a partial payment and continues with the core flow. */ void payment_fail(struct payment *p, const char *fmt, ...) PRINTF_FMT(2,3); +/* Fails a payment process by setting the root payment to + * aborted. This will cause all subpayments to terminate as soon as + * they can, and sets the root failreason so we have a sensible error + * message. The failreason is overwritten if it is already set, since + * we probably know better what happened in the modifier.. */ +void payment_abort(struct payment *p, const char *fmt, ...) PRINTF_FMT(2,3); + struct payment *payment_root(struct payment *p); struct payment_tree_result payment_collect_result(struct payment *p);