Browse Source

plugins/pay: use proper JSON construction for failure paths.

The string cut & paste hack was nasty; make ->failure a json_out
object so we can splice it in properly.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
pull/2938/head
Rusty Russell 6 years ago
parent
commit
0e336ac252
  1. 149
      plugins/pay.c

149
plugins/pay.c

@ -31,7 +31,7 @@ struct pay_attempt {
/* Did we actually try to send a payment? */
bool sendpay;
/* The failure result (NULL on success) */
const char *failure;
struct json_out *failure;
/* The non-failure result (NULL on failure) */
const char *result;
};
@ -112,37 +112,6 @@ static struct pay_attempt *current_attempt(struct pay_command *pc)
return &pc->ps->attempts[tal_count(pc->ps->attempts)-1];
}
PRINTF_FMT(2,3) static void attempt_failed_fmt(struct pay_command *pc, const char *fmt, ...)
{
struct pay_attempt *attempt = current_attempt(pc);
va_list ap;
va_start(ap,fmt);
attempt->failure = tal_vfmt(pc->ps->attempts, fmt, ap);
attempt->end = time_now();
va_end(ap);
}
static void attempt_failed_tok(struct pay_command *pc, const char *method,
const char *buf, const jsmntok_t *errtok)
{
const jsmntok_t *msg = json_get_member(buf, errtok, "message");
if (msg)
attempt_failed_fmt(pc, "%.*sCall to %s:%.*s",
msg->start - errtok->start,
buf + errtok->start,
method,
errtok->end - msg->start,
buf + msg->start);
else
attempt_failed_fmt(pc,
"{ \"message\": \"Call to %s failed\", %.*s",
method,
errtok->end - errtok->start - 1,
buf + errtok->start + 1);
}
/* Helper to copy JSON object directly into a json_out */
static void json_out_add_raw_len(struct json_out *jout,
const char *fieldname,
@ -161,6 +130,54 @@ static void json_out_add_raw(struct json_out *jout,
json_out_add_raw_len(jout, fieldname, jsonstr, strlen(jsonstr));
}
static struct json_out *failed_start(struct pay_command *pc)
{
struct pay_attempt *attempt = current_attempt(pc);
attempt->end = time_now();
attempt->failure = json_out_new(pc->ps->attempts);
json_out_start(attempt->failure, NULL, '{');
return attempt->failure;
}
static void failed_end(struct json_out *jout)
{
json_out_end(jout, '}');
json_out_finished(jout);
}
/* Copy field and member to output, if it exists: return member */
static const jsmntok_t *copy_member(struct json_out *ret,
const char *buf, const jsmntok_t *obj,
const char *membername)
{
const jsmntok_t *m = json_get_member(buf, obj, membername);
if (!m)
return NULL;
/* Literal copy: it's already JSON escaped, and may be a string. */
json_out_add_raw_len(ret, membername,
json_tok_full(buf, m), json_tok_full_len(m));
return m;
}
/* Copy (and modify) error object. */
static void attempt_failed_tok(struct pay_command *pc, const char *method,
const char *buf, const jsmntok_t *errtok)
{
const jsmntok_t *msg = json_get_member(buf, errtok, "message");
struct json_out *failed = failed_start(pc);
/* Every JSON error response has code and error. */
copy_member(failed, buf, errtok, "code");
json_out_add(failed, "message", true,
"Call to %s: %.*s",
method, msg->end - msg->start,
buf + msg->start);
copy_member(failed, buf, errtok, "data");
failed_end(failed);
}
/* Helper to add a u64. */
static void json_out_add_u64(struct json_out *jout,
const char *fieldname,
@ -218,7 +235,8 @@ static struct command_result *waitsendpay_expired(struct command *cmd,
if (pc->ps->attempts[i].route)
json_out_add_raw(data, "route",
pc->ps->attempts[i].route);
json_out_add_raw(data, "failure", pc->ps->attempts[i].failure);
json_out_add_splice(data, "failure",
pc->ps->attempts[i].failure);
}
json_out_end(data, ']');
json_out_end(data, '}');
@ -494,8 +512,10 @@ static struct command_result *getroute_done(struct command *cmd,
attempt->route = join_routehint(pc->ps->attempts, buf, t,
pc, pc->current_routehint);
if (!attempt->route) {
attempt_failed_fmt(pc,
"{ \"message\": \"Joining routehint gave absurd fee\" }");
struct json_out *failed = failed_start(pc);
json_out_add(failed, "message", true,
"Joining routehint gave absurd fee");
failed_end(failed);
return next_routehint(cmd, pc);
}
} else
@ -522,19 +542,22 @@ static struct command_result *getroute_done(struct command *cmd,
if (amount_msat_greater(fee, pc->exemptfee)
&& feepercent > pc->maxfeepercent) {
const jsmntok_t *charger;
struct json_out *failed;
char *feemsg;
attempt_failed_fmt(pc, "{ \"message\": \"Route wanted fee of %s\" }",
type_to_string(tmpctx, struct amount_msat,
&fee));
feemsg = tal_fmt(pc, "Route wanted fee of %s",
type_to_string(tmpctx, struct amount_msat,
&fee));
failed = failed_start(pc);
json_out_addstr(failed, "message", feemsg);
failed_end(failed);
/* Remember this if we eliminating this causes us to have no
* routes at all! */
if (!pc->expensive_route)
pc->expensive_route
= tal_fmt(pc, "Route wanted fee of %s",
type_to_string(tmpctx,
struct amount_msat,
&fee));
pc->expensive_route = feemsg;
else
tal_free(feemsg);
/* Try excluding most fee-charging channel (unless it's in
* routeboost). */
@ -550,17 +573,20 @@ static struct command_result *getroute_done(struct command *cmd,
if (delay > pc->maxdelay) {
const jsmntok_t *delayer;
struct json_out *failed;
char *feemsg;
attempt_failed_fmt(pc,
"{ \"message\": \"Route wanted delay of %u blocks\" }",
delay);
feemsg = tal_fmt(pc, "Route wanted delay of %u blocks", delay);
failed = failed_start(pc);
json_out_addstr(failed, "message", feemsg);
failed_end(failed);
/* Remember this if we eliminating this causes us to have no
* routes at all! */
if (!pc->expensive_route)
pc->expensive_route
= tal_fmt(pc, "Route wanted delay of %u blocks",
delay);
pc->expensive_route = feemsg;
else
tal_free(failed);
delayer = find_worst_channel(buf, t, "delay", pc->final_cltv);
@ -660,8 +686,12 @@ static struct command_result *start_pay_attempt(struct command *cmd,
if (!route_msatoshi(&msat, pc->msat,
attempt->routehint,
tal_count(attempt->routehint))) {
attempt_failed_fmt(pc,
"{ \"message\": \"Routehint absurd fee\" }");
struct json_out *failed;
failed = failed_start(pc);
json_out_addstr(failed, "message",
"Routehint absurd fee");
failed_end(failed);
return next_routehint(cmd, pc);
}
dest = type_to_string(tmpctx, struct node_id,
@ -1089,7 +1119,7 @@ static void add_attempt(struct json_out *ret,
json_out_add_raw(ret, "route", attempt->route);
if (attempt->failure)
json_out_add_raw(ret, "failure", attempt->failure);
json_out_add_splice(ret, "failure", attempt->failure);
if (attempt->result)
json_out_add_raw(ret, "success", attempt->result);
@ -1150,21 +1180,6 @@ static struct command_result *json_paystatus(struct command *cmd,
return command_success(cmd, ret);
}
/* Copy field and member to output, if it exists: return member */
static const jsmntok_t *copy_member(struct json_out *ret,
const char *buf, const jsmntok_t *obj,
const char *membername)
{
const jsmntok_t *m = json_get_member(buf, obj, membername);
if (!m)
return NULL;
/* Literal copy: it's already JSON escaped, and may be a string. */
json_out_add_raw_len(ret, membername,
json_tok_full(buf, m), json_tok_full_len(m));
return m;
}
static bool attempt_ongoing(const char *buf, const jsmntok_t *b11)
{
struct pay_status *ps;

Loading…
Cancel
Save