paymod: Parse and store the waitsendpay result

This is necessary so we can later aggregate across an entire tree of attempts
and report success or failure to the RPC caller.
This commit is contained in:
Christian Decker 2020-05-14 20:22:40 +02:00
parent 6fb81bf203
commit 4fec969062
2 changed files with 92 additions and 7 deletions

View File

@ -1,6 +1,7 @@
#include <plugins/libplugin-pay.h>
#include <stdio.h>
#include <bitcoin/preimage.h>
#include <ccan/array_size/array_size.h>
#include <ccan/tal/str/str.h>
#include <common/json_stream.h>
@ -15,7 +16,7 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd,
p->modifiers = mods;
p->cmd = cmd;
p->start_time = time_now();
p->partid = partid++;
p->result = NULL;
/* Copy over the relevant pieces of information. */
if (parent != NULL) {
@ -209,16 +210,84 @@ fail:
return tal_free(resp);
}
static struct payment_result *tal_sendpay_result_from_json(const tal_t *ctx,
const char *buffer,
const jsmntok_t *toks)
{
const jsmntok_t *idtok = json_get_member(buffer, toks, "id");
const jsmntok_t *hashtok = json_get_member(buffer, toks, "payment_hash");
const jsmntok_t *partidtok = json_get_member(buffer, toks, "partid");
const jsmntok_t *senttok = json_get_member(buffer, toks, "amount_sent_msat");
const jsmntok_t *statustok = json_get_member(buffer, toks, "status");
const jsmntok_t *preimagetok = json_get_member(buffer, toks, "payment_preimage");
const jsmntok_t *codetok = json_get_member(buffer, toks, "code");
const jsmntok_t *datatok = json_get_member(buffer, toks, "data");
struct payment_result *result;
/* Check if we have an error and need to descend into data to get
* details. */
if (codetok != NULL && datatok != NULL) {
idtok = json_get_member(buffer, datatok, "id");
hashtok = json_get_member(buffer, datatok, "payment_hash");
partidtok = json_get_member(buffer, datatok, "partid");
senttok = json_get_member(buffer, datatok, "amount_sent_msat");
statustok = json_get_member(buffer, datatok, "status");
}
/* Initial sanity checks, all these fields must exist. */
if (idtok == NULL || idtok->type != JSMN_PRIMITIVE ||
hashtok == NULL || hashtok->type != JSMN_STRING ||
senttok == NULL || senttok->type != JSMN_STRING ||
statustok == NULL || statustok->type != JSMN_STRING) {
return NULL;
}
result = tal(ctx, struct payment_result);
json_to_u64(buffer, idtok, &result->id);
json_to_u32(buffer, partidtok, &result->partid);
/* TODO Fetch the payment_hash here */
json_to_msat(buffer, senttok, &result->amount_sent);
if (json_tok_streq(buffer, statustok, "pending")) {
result->state = PAYMENT_PENDING;
} else if (json_tok_streq(buffer, statustok, "complete")) {
result->state = PAYMENT_COMPLETE;
} else if (json_tok_streq(buffer, statustok, "failed")) {
result->state = PAYMENT_FAILED;
} else {
goto fail;
}
if (preimagetok != NULL) {
result->payment_preimage = tal(result, struct preimage);
json_to_preimage(buffer, preimagetok, result->payment_preimage);
}
return result;
fail:
return tal_free(result);
}
static struct command_result *
payment_waitsendpay_finished(struct command *cmd, const char *buffer,
const jsmntok_t *toks, struct payment *p)
{
/* TODO examine the failure and eventually stash exclusions that we
* learned in the payment, so sub-payments can avoid them. We also
* need to store the waitsendpay result so we can mock an overall
* waitsendpay for the root later. */
p->result = tal_sendpay_result_from_json(p, buffer, toks);
if (p->result == NULL)
plugin_err(
p->plugin, "Unable to parse `waitsendpay` result: %.*s",
json_tok_full_len(toks), json_tok_full(buffer, toks));
if (p->result->state == PAYMENT_COMPLETE)
p->step = PAYMENT_STEP_SUCCESS;
else
p->step = PAYMENT_STEP_FAILED;
/* TODO examine the failure and eventually stash exclusions that we
* learned in the payment, so sub-payments can avoid them. */
p->step = PAYMENT_STEP_FAILED;
payment_continue(p);
return command_still_pending(cmd);
}

View File

@ -46,9 +46,23 @@ struct createonion_response {
struct secret *shared_secrets;
};
/* States returned by listsendpays, waitsendpay, etc. */
enum payment_result_state {
PAYMENT_PENDING,
PAYMENT_COMPLETE,
PAYMENT_FAILED,
};
/* A parsed version of the possible outcomes that a sendpay / payment may
* result in. */
* result in. It excludes the redundant fields such as payment_hash and partid
* which are already present in the `struct payment` itself. */
struct payment_result {
/* DB internal id */
u64 id;
u32 partid;
enum payment_result_state state;
struct amount_msat amount_sent;
struct preimage *payment_preimage;
};
/* Relevant information about a local channel so we can exclude them early. */
@ -142,6 +156,8 @@ struct payment {
struct payment_modifier **modifiers;
void **modifier_data;
int current_modifier;
struct payment_result *result;
};
struct payment_modifier {