mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 21:35:11 +01:00
libplugin: update API to use json_out.
We now hand around struct json_out members, rather than using formatted strings, so plugins need to construct them properly. There's no automatic conversion between ' and " any more, so those are eliminated too. pay still uses some manual construction of elements. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
c77e29c403
commit
c585f22711
@ -1,4 +1,5 @@
|
||||
#include <ccan/array_size/array_size.h>
|
||||
#include <ccan/json_out/json_out.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <ccan/time/time.h>
|
||||
#include <common/utils.h>
|
||||
@ -22,10 +23,16 @@ static struct command_result *ignore(struct command *timer,
|
||||
|
||||
static struct command_result *do_clean(void)
|
||||
{
|
||||
u64 age = time_now().ts.tv_sec - expired_by;
|
||||
struct json_out *params = json_out_new(NULL);
|
||||
json_out_start(params, NULL, '{');
|
||||
json_out_add(params, "maxexpirytime", false, "%"PRIu64,
|
||||
time_now().ts.tv_sec - expired_by);
|
||||
json_out_end(params, '}');
|
||||
json_out_finished(params);
|
||||
|
||||
/* FIXME: delexpiredinvoice should be in our plugin too! */
|
||||
return send_outreq(NULL, "delexpiredinvoice", ignore, ignore, NULL,
|
||||
"'maxexpirytime': %"PRIu64, age);
|
||||
take(params));
|
||||
}
|
||||
|
||||
static struct command_result *json_autocleaninvoice(struct command *cmd,
|
||||
@ -46,15 +53,16 @@ static struct command_result *json_autocleaninvoice(struct command *cmd,
|
||||
|
||||
if (cycle_seconds == 0) {
|
||||
tal_free(cleantimer);
|
||||
return command_success(cmd, "'Autoclean timer disabled'");
|
||||
return command_success_str(cmd, "Autoclean timer disabled");
|
||||
}
|
||||
tal_free(cleantimer);
|
||||
cleantimer = plugin_timer(rpc, time_from_sec(cycle_seconds), do_clean);
|
||||
|
||||
return command_success(cmd, tal_fmt(cmd, "'Autocleaning %"PRIu64
|
||||
"-second old invoices every %"PRIu64
|
||||
" seconds'",
|
||||
expired_by, cycle_seconds));
|
||||
return command_success_str(cmd,
|
||||
tal_fmt(cmd, "Autocleaning %"PRIu64
|
||||
"-second old invoices every %"PRIu64
|
||||
" seconds",
|
||||
expired_by, cycle_seconds));
|
||||
}
|
||||
|
||||
static void init(struct plugin_conn *prpc)
|
||||
|
@ -84,6 +84,20 @@ struct command_result *command_param_failed(void)
|
||||
return &complete;
|
||||
}
|
||||
|
||||
struct json_out *json_out_obj(const tal_t *ctx,
|
||||
const char *fieldname,
|
||||
const char *str)
|
||||
{
|
||||
struct json_out *jout = json_out_new(ctx);
|
||||
json_out_start(jout, NULL, '{');
|
||||
if (str)
|
||||
json_out_addstr(jout, fieldname, str);
|
||||
json_out_end(jout, '}');
|
||||
json_out_finished(jout);
|
||||
|
||||
return jout;
|
||||
}
|
||||
|
||||
/* Realloc helper for tal membufs */
|
||||
static void *membuf_tal_realloc(struct membuf *mb, void *rawelems,
|
||||
size_t newsize)
|
||||
@ -190,17 +204,7 @@ static struct command_result *WARN_UNUSED_RESULT end_cmd(struct command *cmd)
|
||||
return &complete;
|
||||
}
|
||||
|
||||
/* FIXME: We promised callers we'd turn ' into ". */
|
||||
static void copy_with_quote_sub(char *dst, const char *src, size_t len)
|
||||
{
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (src[i] == '\'')
|
||||
dst[i] = '"';
|
||||
else
|
||||
dst[i] = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* str is raw JSON from RPC output. */
|
||||
static struct command_result *WARN_UNUSED_RESULT
|
||||
command_done_raw(struct command *cmd,
|
||||
const char *label,
|
||||
@ -208,23 +212,42 @@ command_done_raw(struct command *cmd,
|
||||
{
|
||||
struct json_out *jout = start_json_rpc(cmd, cmd->id);
|
||||
|
||||
copy_with_quote_sub(json_out_member_direct(jout, label, size),
|
||||
str, size);
|
||||
memcpy(json_out_member_direct(jout, label, size), str, size);
|
||||
|
||||
finish_and_send_json(STDOUT_FILENO, jout);
|
||||
return end_cmd(cmd);
|
||||
}
|
||||
|
||||
static struct command_result *WARN_UNUSED_RESULT
|
||||
command_done_ok(struct command *cmd, const char *result)
|
||||
struct command_result *WARN_UNUSED_RESULT
|
||||
command_success(struct command *cmd, const struct json_out *result)
|
||||
{
|
||||
return command_done_raw(cmd, "result", result, strlen(result));
|
||||
struct json_out *jout = start_json_rpc(cmd, cmd->id);
|
||||
|
||||
json_out_add_splice(jout, "result", result);
|
||||
finish_and_send_json(STDOUT_FILENO, jout);
|
||||
return end_cmd(cmd);
|
||||
}
|
||||
|
||||
struct command_result *WARN_UNUSED_RESULT
|
||||
command_success_str(struct command *cmd, const char *str)
|
||||
{
|
||||
struct json_out *jout = start_json_rpc(cmd, cmd->id);
|
||||
|
||||
if (str)
|
||||
json_out_addstr(jout, "result", str);
|
||||
else {
|
||||
/* Use an empty object if they don't want anything. */
|
||||
json_out_start(jout, "result", '{');
|
||||
json_out_end(jout, '}');
|
||||
}
|
||||
finish_and_send_json(STDOUT_FILENO, jout);
|
||||
return end_cmd(cmd);
|
||||
}
|
||||
|
||||
struct command_result *command_done_err(struct command *cmd,
|
||||
int code,
|
||||
const char *errmsg,
|
||||
const char *data)
|
||||
const struct json_out *data)
|
||||
{
|
||||
struct json_out *jout = start_json_rpc(cmd, cmd->id);
|
||||
|
||||
@ -232,11 +255,8 @@ struct command_result *command_done_err(struct command *cmd,
|
||||
json_out_add(jout, "code", false, "%d", code);
|
||||
json_out_addstr(jout, "message", errmsg);
|
||||
|
||||
if (data) {
|
||||
char *p;
|
||||
p = json_out_member_direct(jout, "data", strlen(data));
|
||||
copy_with_quote_sub(p, data, strlen(data));
|
||||
}
|
||||
if (data)
|
||||
json_out_add_splice(jout, "data", data);
|
||||
json_out_end(jout, '}');
|
||||
|
||||
finish_and_send_json(STDOUT_FILENO, jout);
|
||||
@ -250,11 +270,6 @@ struct command_result *timer_complete(void)
|
||||
return &complete;
|
||||
}
|
||||
|
||||
struct command_result *command_success(struct command *cmd, const char *result)
|
||||
{
|
||||
return command_done_raw(cmd, "result", result, strlen(result));
|
||||
}
|
||||
|
||||
struct command_result *forward_error(struct command *cmd,
|
||||
const char *buf,
|
||||
const jsmntok_t *error,
|
||||
@ -342,23 +357,23 @@ static const jsmntok_t *read_rpc_reply(const tal_t *ctx,
|
||||
static struct json_out *start_json_request(const tal_t *ctx,
|
||||
u64 id,
|
||||
const char *method,
|
||||
const char *params)
|
||||
const struct json_out *params TAKES)
|
||||
{
|
||||
struct json_out *jout;
|
||||
|
||||
jout = start_json_rpc(tmpctx, id);
|
||||
json_out_addstr(jout, "method", method);
|
||||
json_out_start(jout, "params", '{');
|
||||
copy_with_quote_sub(json_out_direct(jout, strlen(params)),
|
||||
params, strlen(params));
|
||||
json_out_end(jout, '}');
|
||||
json_out_add_splice(jout, "params", params);
|
||||
if (taken(params))
|
||||
tal_free(params);
|
||||
|
||||
return jout;
|
||||
}
|
||||
|
||||
/* Synchronous routine to send command and extract single field from response */
|
||||
const char *rpc_delve(const tal_t *ctx,
|
||||
const char *method, const char *params,
|
||||
const char *method,
|
||||
const struct json_out *params TAKES,
|
||||
struct plugin_conn *rpc, const char *guide)
|
||||
{
|
||||
bool error;
|
||||
@ -435,12 +450,10 @@ send_outreq_(struct command *cmd,
|
||||
const jsmntok_t *result,
|
||||
void *arg),
|
||||
void *arg,
|
||||
const char *paramfmt_single_ticks, ...)
|
||||
const struct json_out *params TAKES)
|
||||
{
|
||||
va_list ap;
|
||||
struct json_out *jout;
|
||||
struct out_req *out;
|
||||
char *params;
|
||||
|
||||
out = tal(cmd, struct out_req);
|
||||
out->id = next_outreq_id++;
|
||||
@ -450,10 +463,6 @@ send_outreq_(struct command *cmd,
|
||||
out->arg = arg;
|
||||
uintmap_add(&out_reqs, out->id, out);
|
||||
|
||||
va_start(ap, paramfmt_single_ticks);
|
||||
params = tal_vfmt(tmpctx, paramfmt_single_ticks, ap);
|
||||
va_end(ap);
|
||||
|
||||
jout = start_json_request(tmpctx, out->id, method, params);
|
||||
finish_and_send_json(rpc_conn.fd, jout);
|
||||
|
||||
@ -467,8 +476,6 @@ handle_getmanifest(struct command *getmanifest_cmd,
|
||||
const struct plugin_option *opts)
|
||||
{
|
||||
struct json_out *params = json_out_new(tmpctx);
|
||||
size_t len;
|
||||
const char *p;
|
||||
|
||||
json_out_start(params, NULL, '{');
|
||||
json_out_start(params, "options", '[');
|
||||
@ -497,8 +504,7 @@ handle_getmanifest(struct command *getmanifest_cmd,
|
||||
json_out_end(params, '}');
|
||||
json_out_finished(params);
|
||||
|
||||
p = json_out_contents(params, &len);
|
||||
return command_done_raw(getmanifest_cmd, "result", p, len);
|
||||
return command_success(getmanifest_cmd, params);
|
||||
}
|
||||
|
||||
static struct command_result *handle_init(struct command *init_cmd,
|
||||
@ -511,6 +517,7 @@ static struct command_result *handle_init(struct command *init_cmd,
|
||||
struct sockaddr_un addr;
|
||||
size_t i;
|
||||
char *dir;
|
||||
struct json_out *param_obj;
|
||||
|
||||
/* Move into lightning directory: other files are relative */
|
||||
dirtok = json_delve(buf, params, ".configuration.lightning-dir");
|
||||
@ -533,8 +540,9 @@ static struct command_result *handle_init(struct command *init_cmd,
|
||||
rpctok->end - rpctok->start, buf + rpctok->start,
|
||||
strerror(errno));
|
||||
|
||||
param_obj = json_out_obj(NULL, "config", "allow-deprecated-apis");
|
||||
deprecated_apis = streq(rpc_delve(tmpctx, "listconfigs",
|
||||
"'config': 'allow-deprecated-apis'",
|
||||
take(param_obj),
|
||||
&rpc_conn,
|
||||
".allow-deprecated-apis"),
|
||||
"true");
|
||||
@ -558,7 +566,7 @@ static struct command_result *handle_init(struct command *init_cmd,
|
||||
if (init)
|
||||
init(&rpc_conn);
|
||||
|
||||
return command_done_ok(init_cmd, "{}");
|
||||
return command_success_str(init_cmd, NULL);
|
||||
}
|
||||
|
||||
char *u64_option(const char *arg, u64 *i)
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <common/status_levels.h>
|
||||
|
||||
struct command;
|
||||
struct json_out;
|
||||
struct plugin_conn;
|
||||
|
||||
extern bool deprecated_apis;
|
||||
@ -35,6 +36,12 @@ struct plugin_option {
|
||||
void *arg;
|
||||
};
|
||||
|
||||
/* Helper to create a zero or single-value JSON object; if @str is NULL,
|
||||
* object is empty. */
|
||||
struct json_out *json_out_obj(const tal_t *ctx,
|
||||
const char *fieldname,
|
||||
const char *str);
|
||||
|
||||
/* Return this iff the param() call failed in your handler. */
|
||||
struct command_result *command_param_failed(void);
|
||||
|
||||
@ -42,27 +49,34 @@ struct command_result *command_param_failed(void);
|
||||
void NORETURN plugin_err(const char *fmt, ...);
|
||||
|
||||
/* This command is finished, here's a detailed error; @cmd cannot be
|
||||
* NULL, data can be NULL. */
|
||||
* NULL, data can be NULL; otherwise it must be a JSON object. */
|
||||
struct command_result *WARN_UNUSED_RESULT
|
||||
command_done_err(struct command *cmd,
|
||||
int code,
|
||||
const char *errmsg,
|
||||
const char *data);
|
||||
const struct json_out *data);
|
||||
|
||||
/* This command is finished, here's the success msg; @cmd cannot be NULL. */
|
||||
/* This command is finished, here's the result object; @cmd cannot be NULL. */
|
||||
struct command_result *WARN_UNUSED_RESULT
|
||||
command_success(struct command *cmd, const char *result);
|
||||
command_success(struct command *cmd, const struct json_out *result);
|
||||
|
||||
/* Simple version where we just want to send a string, or NULL means an empty
|
||||
* result object. @cmd cannot be NULL. */
|
||||
struct command_result *WARN_UNUSED_RESULT
|
||||
command_success_str(struct command *cmd, const char *str);
|
||||
|
||||
/* Synchronous helper to send command and extract single field from
|
||||
* response; can only be used in init callback. */
|
||||
const char *rpc_delve(const tal_t *ctx,
|
||||
const char *method, const char *params,
|
||||
const char *method,
|
||||
const struct json_out *params TAKES,
|
||||
struct plugin_conn *rpc, const char *guide);
|
||||
|
||||
/* Async rpc request. For convenience, and single ' are turned into ".
|
||||
/* Async rpc request.
|
||||
* @cmd can be NULL if we're coming from a timer callback.
|
||||
* @params can be NULL, otherwise it's an array or object.
|
||||
*/
|
||||
PRINTF_FMT(6,7) struct command_result *
|
||||
struct command_result *
|
||||
send_outreq_(struct command *cmd,
|
||||
const char *method,
|
||||
struct command_result *(*cb)(struct command *command,
|
||||
@ -74,9 +88,9 @@ send_outreq_(struct command *cmd,
|
||||
const jsmntok_t *result,
|
||||
void *arg),
|
||||
void *arg,
|
||||
const char *paramfmt_single_ticks, ...);
|
||||
const struct json_out *params TAKES);
|
||||
|
||||
#define send_outreq(cmd, method, cb, errcb, arg, ...) \
|
||||
#define send_outreq(cmd, method, cb, errcb, arg, params) \
|
||||
send_outreq_((cmd), (method), \
|
||||
typesafe_cb_preargs(struct command_result *, void *, \
|
||||
(cb), (arg), \
|
||||
@ -88,7 +102,7 @@ send_outreq_(struct command *cmd,
|
||||
struct command *command, \
|
||||
const char *buf, \
|
||||
const jsmntok_t *result), \
|
||||
(arg), __VA_ARGS__)
|
||||
(arg), (params))
|
||||
|
||||
/* Callback to just forward error and close request; @cmd cannot be NULL */
|
||||
struct command_result *forward_error(struct command *cmd,
|
||||
|
344
plugins/pay.c
344
plugins/pay.c
@ -1,6 +1,7 @@
|
||||
#include <ccan/array_size/array_size.h>
|
||||
#include <ccan/cast/cast.h>
|
||||
#include <ccan/intmap/intmap.h>
|
||||
#include <ccan/json_out/json_out.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <ccan/time/time.h>
|
||||
#include <common/amount.h>
|
||||
@ -136,12 +137,38 @@ static void attempt_failed_tok(struct pay_command *pc, const char *method,
|
||||
buf + msg->start);
|
||||
else
|
||||
attempt_failed_fmt(pc,
|
||||
"{ 'message': 'Call to %s failed', %.*s",
|
||||
"{ \"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,
|
||||
const char *jsonstr, size_t len)
|
||||
{
|
||||
char *p;
|
||||
|
||||
p = json_out_member_direct(jout, fieldname, len);
|
||||
memcpy(p, jsonstr, len);
|
||||
}
|
||||
|
||||
static void json_out_add_raw(struct json_out *jout,
|
||||
const char *fieldname,
|
||||
const char *jsonstr)
|
||||
{
|
||||
json_out_add_raw_len(jout, fieldname, jsonstr, strlen(jsonstr));
|
||||
}
|
||||
|
||||
/* Helper to add a u64. */
|
||||
static void json_out_add_u64(struct json_out *jout,
|
||||
const char *fieldname,
|
||||
u64 val)
|
||||
{
|
||||
json_out_add(jout, fieldname, false, "%"PRIu64, val);
|
||||
}
|
||||
|
||||
static struct command_result *start_pay_attempt(struct command *cmd,
|
||||
struct pay_command *pc,
|
||||
const char *fmt, ...);
|
||||
@ -178,24 +205,23 @@ static size_t count_sendpays(const struct pay_attempt *attempts)
|
||||
static struct command_result *waitsendpay_expired(struct command *cmd,
|
||||
struct pay_command *pc)
|
||||
{
|
||||
char *errmsg, *data;
|
||||
char *errmsg;
|
||||
struct json_out *data;
|
||||
size_t num_attempts = count_sendpays(pc->ps->attempts);
|
||||
|
||||
errmsg = tal_fmt(pc, "Gave up after %zu attempt%s: see paystatus",
|
||||
num_attempts, num_attempts == 1 ? "" : "s");
|
||||
data = tal_strdup(pc, "{ 'attempts': [ ");
|
||||
data = json_out_new(NULL);
|
||||
json_out_start(data, NULL, '{');
|
||||
json_out_start(data, "attempts", '[');
|
||||
for (size_t i = 0; i < tal_count(pc->ps->attempts); i++) {
|
||||
if (pc->ps->attempts[i].route)
|
||||
tal_append_fmt(&data, "%s { 'route': %s,\n 'failure': %s\n }",
|
||||
i == 0 ? "" : ",",
|
||||
pc->ps->attempts[i].route,
|
||||
pc->ps->attempts[i].failure);
|
||||
else
|
||||
tal_append_fmt(&data, "%s { 'failure': %s\n }",
|
||||
i == 0 ? "" : ",",
|
||||
pc->ps->attempts[i].failure);
|
||||
json_out_add_raw(data, "route",
|
||||
pc->ps->attempts[i].route);
|
||||
json_out_add_raw(data, "failure", pc->ps->attempts[i].failure);
|
||||
}
|
||||
tal_append_fmt(&data, "] }");
|
||||
json_out_end(data, ']');
|
||||
json_out_end(data, '}');
|
||||
return command_done_err(cmd, PAY_STOPPED_RETRYING, errmsg, data);
|
||||
}
|
||||
|
||||
@ -315,8 +341,8 @@ static struct command_result *sendpay_done(struct command *cmd,
|
||||
{
|
||||
return send_outreq(cmd, "waitsendpay",
|
||||
waitsendpay_done, waitsendpay_error, pc,
|
||||
"'payment_hash': '%s'",
|
||||
pc->payment_hash);
|
||||
take(json_out_obj(NULL, "payment_hash",
|
||||
pc->payment_hash)));
|
||||
}
|
||||
|
||||
/* Calculate how many millisatoshi we need at the start of this route
|
||||
@ -375,10 +401,10 @@ static const char *join_routehint(const tal_t *ctx,
|
||||
return tal_free(ret);
|
||||
|
||||
tal_append_fmt(&ret, ", {"
|
||||
" 'id': '%s',"
|
||||
" 'channel': '%s',"
|
||||
" 'msatoshi': '%s',"
|
||||
" 'delay': %u }",
|
||||
" \"id\": \"%s\","
|
||||
" \"channel\": \"%s\","
|
||||
" \"msatoshi\": \"%s\","
|
||||
" \"delay\": %u }",
|
||||
/* pubkey of *destination* */
|
||||
route_pubkey(tmpctx, pc, routehint, i + 1),
|
||||
type_to_string(tmpctx, struct short_channel_id,
|
||||
@ -455,10 +481,10 @@ static struct command_result *getroute_done(struct command *cmd,
|
||||
{
|
||||
struct pay_attempt *attempt = current_attempt(pc);
|
||||
const jsmntok_t *t = json_get_member(buf, result, "route");
|
||||
char *json_desc;
|
||||
struct amount_msat fee;
|
||||
u32 delay;
|
||||
double feepercent;
|
||||
struct json_out *params;
|
||||
|
||||
if (!t)
|
||||
plugin_err("getroute gave no 'route'? '%.*s'",
|
||||
@ -469,7 +495,7 @@ static struct command_result *getroute_done(struct command *cmd,
|
||||
pc, pc->current_routehint);
|
||||
if (!attempt->route) {
|
||||
attempt_failed_fmt(pc,
|
||||
"{ 'message': 'Joining routehint gave absurd fee' }");
|
||||
"{ \"message\": \"Joining routehint gave absurd fee\" }");
|
||||
return next_routehint(cmd, pc);
|
||||
}
|
||||
} else
|
||||
@ -497,7 +523,7 @@ static struct command_result *getroute_done(struct command *cmd,
|
||||
&& feepercent > pc->maxfeepercent) {
|
||||
const jsmntok_t *charger;
|
||||
|
||||
attempt_failed_fmt(pc, "{ 'message': 'Route wanted fee of %s' }",
|
||||
attempt_failed_fmt(pc, "{ \"message\": \"Route wanted fee of %s\" }",
|
||||
type_to_string(tmpctx, struct amount_msat,
|
||||
&fee));
|
||||
|
||||
@ -526,7 +552,7 @@ static struct command_result *getroute_done(struct command *cmd,
|
||||
const jsmntok_t *delayer;
|
||||
|
||||
attempt_failed_fmt(pc,
|
||||
"{ 'message': 'Route wanted delay of %u blocks' }",
|
||||
"{ \"message\": \"Route wanted delay of %u blocks\" }",
|
||||
delay);
|
||||
|
||||
/* Remember this if we eliminating this causes us to have no
|
||||
@ -549,18 +575,18 @@ static struct command_result *getroute_done(struct command *cmd,
|
||||
return next_routehint(cmd, pc);
|
||||
}
|
||||
|
||||
if (pc->label)
|
||||
json_desc = tal_fmt(pc, ", 'label': '%s'", pc->label);
|
||||
else
|
||||
json_desc = "";
|
||||
|
||||
attempt->sendpay = true;
|
||||
params = json_out_new(NULL);
|
||||
json_out_start(params, NULL, '{');
|
||||
json_out_add_raw(params, "route", attempt->route);
|
||||
json_out_add(params, "payment_hash", true, "%s", pc->payment_hash);
|
||||
json_out_add(params, "bolt11", true, "%s", pc->ps->bolt11);
|
||||
if (pc->label)
|
||||
json_out_add(params, "label", true, "%s", pc->label);
|
||||
json_out_end(params, '}');
|
||||
|
||||
return send_outreq(cmd, "sendpay", sendpay_done, sendpay_error, pc,
|
||||
"'route': %s, 'payment_hash': '%s', 'bolt11': '%s'%s",
|
||||
attempt->route,
|
||||
pc->payment_hash,
|
||||
pc->ps->bolt11,
|
||||
json_desc);
|
||||
take(params));
|
||||
|
||||
}
|
||||
|
||||
@ -600,7 +626,6 @@ static struct command_result *start_pay_attempt(struct command *cmd,
|
||||
struct pay_command *pc,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
char *exclude;
|
||||
struct amount_msat msat;
|
||||
const char *dest;
|
||||
size_t max_hops = ROUTING_MAX_HOPS;
|
||||
@ -608,6 +633,7 @@ static struct command_result *start_pay_attempt(struct command *cmd,
|
||||
struct pay_attempt *attempt;
|
||||
va_list ap;
|
||||
size_t n;
|
||||
struct json_out *params;
|
||||
|
||||
n = tal_count(pc->ps->attempts);
|
||||
tal_resize(&pc->ps->attempts, n+1);
|
||||
@ -627,17 +653,6 @@ static struct command_result *start_pay_attempt(struct command *cmd,
|
||||
|
||||
/* routehint set below. */
|
||||
|
||||
if (tal_count(pc->excludes) != 0) {
|
||||
exclude = tal_strdup(tmpctx, ",'exclude': [");
|
||||
for (size_t i = 0; i < tal_count(pc->excludes); i++)
|
||||
/* JSON.org grammar doesn't allow trailing , */
|
||||
tal_append_fmt(&exclude, "%s %s",
|
||||
i == 0 ? "" : ",",
|
||||
pc->excludes[i]);
|
||||
tal_append_fmt(&exclude, "]");
|
||||
} else
|
||||
exclude = "";
|
||||
|
||||
/* If we have a routehint, try that first; we need to do extra
|
||||
* checks that it meets our criteria though. */
|
||||
if (pc->current_routehint) {
|
||||
@ -646,7 +661,7 @@ static struct command_result *start_pay_attempt(struct command *cmd,
|
||||
attempt->routehint,
|
||||
tal_count(attempt->routehint))) {
|
||||
attempt_failed_fmt(pc,
|
||||
"{ 'message': 'Routehint absurd fee' }");
|
||||
"{ \"message\": \"Routehint absurd fee\" }");
|
||||
return next_routehint(cmd, pc);
|
||||
}
|
||||
dest = type_to_string(tmpctx, struct node_id,
|
||||
@ -663,15 +678,24 @@ static struct command_result *start_pay_attempt(struct command *cmd,
|
||||
}
|
||||
|
||||
/* OK, ask for route to destination */
|
||||
params = json_out_new(NULL);
|
||||
json_out_start(params, NULL, '{');
|
||||
json_out_addstr(params, "id", dest);
|
||||
json_out_addstr(params, "msatoshi",
|
||||
type_to_string(tmpctx, struct amount_msat, &msat));
|
||||
json_out_add_u64(params, "cltv", cltv);
|
||||
json_out_add_u64(params, "maxhops", max_hops);
|
||||
json_out_add(params, "riskfactor", false, "%f", pc->riskfactor);
|
||||
if (tal_count(pc->excludes) != 0) {
|
||||
json_out_start(params, "exclude", '[');
|
||||
for (size_t i = 0; i < tal_count(pc->excludes); i++)
|
||||
json_out_addstr(params, NULL, pc->excludes[i]);
|
||||
json_out_end(params, ']');
|
||||
}
|
||||
json_out_end(params, '}');
|
||||
|
||||
return send_outreq(cmd, "getroute", getroute_done, getroute_error, pc,
|
||||
"'id': '%s',"
|
||||
"'msatoshi': '%s',"
|
||||
"'cltv': %u,"
|
||||
"'maxhops': %zu,"
|
||||
"'riskfactor': %f%s",
|
||||
dest,
|
||||
type_to_string(tmpctx, struct amount_msat, &msat),
|
||||
cltv, max_hops, pc->riskfactor, exclude);
|
||||
take(params));
|
||||
}
|
||||
|
||||
/* BOLT #7:
|
||||
@ -751,7 +775,7 @@ static struct command_result *shadow_route(struct command *cmd,
|
||||
|
||||
return send_outreq(cmd, "listchannels",
|
||||
add_shadow_route, forward_error, pc,
|
||||
"'source' : '%s'", pc->shadow_dest);
|
||||
take(json_out_obj(NULL, "source", pc->shadow_dest)));
|
||||
}
|
||||
|
||||
/* gossipd doesn't know much about the current state of channels; here we
|
||||
@ -996,10 +1020,9 @@ static struct command_result *json_pay(struct command *cmd,
|
||||
pc->routehints = filter_routehints(pc, b11->routes);
|
||||
pc->expensive_route = NULL;
|
||||
|
||||
/* Get capacities of local channels. */
|
||||
/* Get capacities of local channels (no parameters) */
|
||||
return send_outreq(cmd, "listpeers", listpeers_done, forward_error, pc,
|
||||
/* gcc doesn't like zero-length format strings! */
|
||||
" ");
|
||||
take(json_out_obj(NULL, NULL, NULL)));
|
||||
}
|
||||
|
||||
/* FIXME: Add this to ccan/time? */
|
||||
@ -1014,7 +1037,7 @@ static void utc_timestring(const struct timeabs *time, char str[UTC_TIMELEN])
|
||||
(int) time->ts.tv_nsec / 1000000);
|
||||
}
|
||||
|
||||
static void add_attempt(char **ret,
|
||||
static void add_attempt(struct json_out *ret,
|
||||
const struct pay_status *ps,
|
||||
const struct pay_attempt *attempt)
|
||||
{
|
||||
@ -1022,62 +1045,56 @@ static void add_attempt(char **ret,
|
||||
|
||||
utc_timestring(&attempt->start, timestr);
|
||||
|
||||
tal_append_fmt(ret, "{ 'strategy': '%s',"
|
||||
" 'start_time': '%s',"
|
||||
" 'age_in_seconds': %"PRIu64,
|
||||
attempt->why,
|
||||
timestr,
|
||||
time_to_sec(time_between(time_now(), attempt->start)));
|
||||
json_out_start(ret, NULL, '{');
|
||||
json_out_addstr(ret, "strategy", attempt->why);
|
||||
json_out_addstr(ret, "start_time", timestr);
|
||||
json_out_add_u64(ret, "age_in_seconds",
|
||||
time_to_sec(time_between(time_now(), attempt->start)));
|
||||
if (attempt->result || attempt->failure) {
|
||||
utc_timestring(&attempt->end, timestr);
|
||||
tal_append_fmt(ret, ", 'end_time': '%s'"
|
||||
", 'duration_in_seconds': %"PRIu64,
|
||||
timestr,
|
||||
time_to_sec(time_between(attempt->end,
|
||||
attempt->start)));
|
||||
json_out_addstr(ret, "end_time", timestr);
|
||||
json_out_add_u64(ret, "duration_in_seconds",
|
||||
time_to_sec(time_between(attempt->end,
|
||||
attempt->start)));
|
||||
}
|
||||
if (tal_count(attempt->routehint)) {
|
||||
tal_append_fmt(ret, ", 'routehint': [");
|
||||
json_out_start(ret, "routehint", '[');
|
||||
for (size_t i = 0; i < tal_count(attempt->routehint); i++) {
|
||||
tal_append_fmt(ret, "%s{"
|
||||
" 'id': '%s',"
|
||||
" 'channel': '%s',"
|
||||
" 'fee_base_msat': %u,"
|
||||
" 'fee_proportional_millionths': %u,"
|
||||
" 'cltv_expiry_delta': %u }",
|
||||
i == 0 ? "" : ", ",
|
||||
type_to_string(tmpctx, struct node_id,
|
||||
&attempt->routehint[i].pubkey),
|
||||
type_to_string(tmpctx,
|
||||
struct short_channel_id,
|
||||
&attempt->routehint[i].short_channel_id),
|
||||
attempt->routehint[i].fee_base_msat,
|
||||
attempt->routehint[i].fee_proportional_millionths,
|
||||
attempt->routehint[i].cltv_expiry_delta);
|
||||
json_out_start(ret, NULL, '{');
|
||||
json_out_addstr(ret, "id",
|
||||
type_to_string(tmpctx, struct node_id,
|
||||
&attempt->routehint[i].pubkey));
|
||||
json_out_addstr(ret, "channel",
|
||||
type_to_string(tmpctx,
|
||||
struct short_channel_id,
|
||||
&attempt->routehint[i].short_channel_id));
|
||||
json_out_add_u64(ret, "fee_base_msat",
|
||||
attempt->routehint[i].fee_base_msat);
|
||||
json_out_add_u64(ret, "fee_proportional_millionths",
|
||||
attempt->routehint[i].fee_proportional_millionths);
|
||||
json_out_add_u64(ret, "cltv_expiry_delta",
|
||||
attempt->routehint[i].cltv_expiry_delta);
|
||||
json_out_end(ret, '}');
|
||||
}
|
||||
tal_append_fmt(ret, "]");
|
||||
json_out_end(ret, ']');
|
||||
}
|
||||
if (tal_count(attempt->excludes)) {
|
||||
for (size_t i = 0; i < tal_count(attempt->excludes); i++) {
|
||||
if (i == 0)
|
||||
tal_append_fmt(ret, ", 'excluded_channels': [");
|
||||
else
|
||||
tal_append_fmt(ret, ", ");
|
||||
tal_append_fmt(ret, "'%s'", attempt->excludes[i]);
|
||||
}
|
||||
tal_append_fmt(ret, "]");
|
||||
json_out_start(ret, "excluded_channels", '[');
|
||||
for (size_t i = 0; i < tal_count(attempt->excludes); i++)
|
||||
json_out_addstr(ret, NULL, attempt->excludes[i]);
|
||||
json_out_end(ret, ']');
|
||||
}
|
||||
|
||||
if (attempt->route)
|
||||
tal_append_fmt(ret, ", 'route': %s", attempt->route);
|
||||
json_out_add_raw(ret, "route", attempt->route);
|
||||
|
||||
if (attempt->failure)
|
||||
tal_append_fmt(ret, ", 'failure': %s", attempt->failure);
|
||||
json_out_add_raw(ret, "failure", attempt->failure);
|
||||
|
||||
if (attempt->result)
|
||||
tal_append_fmt(ret, ", 'success': %s", attempt->result);
|
||||
json_out_add_raw(ret, "success", attempt->result);
|
||||
|
||||
tal_append_fmt(ret, "}");
|
||||
json_out_end(ret, '}');
|
||||
}
|
||||
|
||||
static struct command_result *json_paystatus(struct command *cmd,
|
||||
@ -1086,71 +1103,65 @@ static struct command_result *json_paystatus(struct command *cmd,
|
||||
{
|
||||
struct pay_status *ps;
|
||||
const char *b11str;
|
||||
char *ret;
|
||||
bool some = false;
|
||||
struct json_out *ret;
|
||||
|
||||
if (!param(cmd, buf, params,
|
||||
p_opt("bolt11", param_string, &b11str),
|
||||
NULL))
|
||||
return NULL;
|
||||
|
||||
ret = tal_fmt(cmd, "{ 'pay': [");
|
||||
ret = json_out_new(NULL);
|
||||
json_out_start(ret, NULL, '{');
|
||||
json_out_start(ret, "pay", '[');
|
||||
|
||||
/* FIXME: Index by bolt11 string! */
|
||||
list_for_each(&pay_status, ps, list) {
|
||||
if (b11str && !streq(b11str, ps->bolt11))
|
||||
continue;
|
||||
|
||||
if (some)
|
||||
tal_append_fmt(&ret, ",\n");
|
||||
some = true;
|
||||
|
||||
tal_append_fmt(&ret, "{ 'bolt11': '%s',"
|
||||
" 'msatoshi': %"PRIu64", "
|
||||
" 'amount_msat': '%s', "
|
||||
" 'destination': '%s'",
|
||||
ps->bolt11,
|
||||
ps->msat.millisatoshis, /* Raw: JSON */
|
||||
json_out_start(ret, NULL, '{');
|
||||
json_out_addstr(ret, "bolt11", ps->bolt11);
|
||||
json_out_add_u64(ret, "msatoshi",
|
||||
ps->msat.millisatoshis); /* Raw: JSON */
|
||||
json_out_addstr(ret, "amount_msat",
|
||||
type_to_string(tmpctx, struct amount_msat,
|
||||
&ps->msat), ps->dest);
|
||||
&ps->msat));
|
||||
json_out_addstr(ret, "destination", ps->dest);
|
||||
if (ps->label)
|
||||
tal_append_fmt(&ret, ", 'label': '%s'", ps->label);
|
||||
json_out_addstr(ret, "label", ps->label);
|
||||
if (ps->routehint_modifications)
|
||||
tal_append_fmt(&ret, ", 'routehint_modifications': '%s'",
|
||||
ps->routehint_modifications);
|
||||
json_out_addstr(ret, "routehint_modifications",
|
||||
ps->routehint_modifications);
|
||||
if (ps->shadow && !streq(ps->shadow, ""))
|
||||
tal_append_fmt(&ret, ", 'shadow': '%s'", ps->shadow);
|
||||
json_out_addstr(ret, "shadow", ps->shadow);
|
||||
if (ps->exclusions)
|
||||
tal_append_fmt(&ret, ", 'local_exclusions': '%s'",
|
||||
ps->exclusions);
|
||||
json_out_addstr(ret, "local_exclusions", ps->exclusions);
|
||||
|
||||
assert(tal_count(ps->attempts));
|
||||
for (size_t i = 0; i < tal_count(ps->attempts); i++) {
|
||||
if (i == 0)
|
||||
tal_append_fmt(&ret, ", 'attempts': [");
|
||||
else
|
||||
tal_append_fmt(&ret, ",");
|
||||
|
||||
add_attempt(&ret, ps, &ps->attempts[i]);
|
||||
}
|
||||
tal_append_fmt(&ret, "] }");
|
||||
json_out_start(ret, "attempts", '[');
|
||||
for (size_t i = 0; i < tal_count(ps->attempts); i++)
|
||||
add_attempt(ret, ps, &ps->attempts[i]);
|
||||
json_out_end(ret, ']');
|
||||
json_out_end(ret, '}');
|
||||
}
|
||||
tal_append_fmt(&ret, "] }");
|
||||
json_out_end(ret, ']');
|
||||
json_out_end(ret, '}');
|
||||
|
||||
return command_success(cmd, ret);
|
||||
}
|
||||
|
||||
static const jsmntok_t *copy_member(char **ret,
|
||||
const char *buf,
|
||||
const jsmntok_t *result,
|
||||
const char *membername,
|
||||
const char *term)
|
||||
/* 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, result, membername);
|
||||
if (m)
|
||||
tal_append_fmt(ret, "'%s': %.*s%s",
|
||||
membername,
|
||||
json_tok_full_len(m), json_tok_full(buf, m),
|
||||
term);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1175,33 +1186,30 @@ static struct command_result *listsendpays_done(struct command *cmd,
|
||||
{
|
||||
size_t i;
|
||||
const jsmntok_t *t, *arr;
|
||||
char *ret;
|
||||
bool some = false;
|
||||
struct json_out *ret;
|
||||
|
||||
arr = json_get_member(buf, result, "payments");
|
||||
if (!arr || arr->type != JSMN_ARRAY)
|
||||
return command_fail(cmd, LIGHTNINGD,
|
||||
"Unexpected non-array result from listsendpays");
|
||||
|
||||
ret = tal_fmt(cmd, "{ 'pays': [");
|
||||
ret = json_out_new(NULL);
|
||||
json_out_start(ret, NULL, '{');
|
||||
json_out_start(ret, "pays", '[');
|
||||
json_for_each_arr(i, t, arr) {
|
||||
const jsmntok_t *status, *b11;
|
||||
|
||||
if (some)
|
||||
tal_append_fmt(&ret, ",\n");
|
||||
some = true;
|
||||
|
||||
tal_append_fmt(&ret, "{");
|
||||
json_out_start(ret, NULL, '{');
|
||||
/* Old payments didn't have bolt11 field */
|
||||
b11 = copy_member(&ret, buf, t, "bolt11", ",");
|
||||
b11 = copy_member(ret, buf, t, "bolt11");
|
||||
if (!b11) {
|
||||
if (b11str) {
|
||||
/* If it's a single query, we can fake it */
|
||||
tal_append_fmt(&ret, "'bolt11': '%s',", b11str);
|
||||
json_out_addstr(ret, "bolt11", b11str);
|
||||
} else {
|
||||
copy_member(&ret, buf, t, "payment_hash", ",");
|
||||
copy_member(&ret, buf, t, "destination", ",");
|
||||
copy_member(&ret, buf, t, "amount_msat", ",");
|
||||
copy_member(ret, buf, t, "payment_hash");
|
||||
copy_member(ret, buf, t, "destination");
|
||||
copy_member(ret, buf, t, "amount_msat");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1210,19 +1218,20 @@ static struct command_result *listsendpays_done(struct command *cmd,
|
||||
if (status) {
|
||||
if (json_tok_streq(buf, status, "failed")
|
||||
&& attempt_ongoing(buf, b11)) {
|
||||
tal_append_fmt(&ret, "'status': 'pending',");
|
||||
json_out_addstr(ret, "status", "pending");
|
||||
} else {
|
||||
copy_member(&ret, buf, t, "status", ",");
|
||||
copy_member(ret, buf, t, "status");
|
||||
if (json_tok_streq(buf, status, "complete"))
|
||||
copy_member(&ret, buf, t,
|
||||
"payment_preimage", ",");
|
||||
copy_member(ret, buf, t,
|
||||
"payment_preimage");
|
||||
}
|
||||
}
|
||||
copy_member(&ret, buf, t, "label", ",");
|
||||
copy_member(&ret, buf, t, "amount_sent_msat", "");
|
||||
tal_append_fmt(&ret, "}");
|
||||
copy_member(ret, buf, t, "label");
|
||||
copy_member(ret, buf, t, "amount_sent_msat");
|
||||
json_out_end(ret, '}');
|
||||
}
|
||||
tal_append_fmt(&ret, "] }");
|
||||
json_out_end(ret, ']');
|
||||
json_out_end(ret, '}');
|
||||
return command_success(cmd, ret);
|
||||
}
|
||||
|
||||
@ -1230,7 +1239,7 @@ static struct command_result *json_listpays(struct command *cmd,
|
||||
const char *buf,
|
||||
const jsmntok_t *params)
|
||||
{
|
||||
const char *b11str, *paramstr;
|
||||
const char *b11str;
|
||||
|
||||
/* FIXME: would be nice to parse as a bolt11 so check worked in future */
|
||||
if (!param(cmd, buf, params,
|
||||
@ -1238,26 +1247,25 @@ static struct command_result *json_listpays(struct command *cmd,
|
||||
NULL))
|
||||
return NULL;
|
||||
|
||||
if (b11str)
|
||||
paramstr = tal_fmt(tmpctx, "'bolt11' : '%s'", b11str);
|
||||
else
|
||||
paramstr = "";
|
||||
return send_outreq(cmd, "listsendpays",
|
||||
listsendpays_done, forward_error,
|
||||
cast_const(char *, b11str),
|
||||
"%s", paramstr);
|
||||
/* Neatly returns empty object if b11str is NULL */
|
||||
take(json_out_obj(NULL, "bolt11", b11str)));
|
||||
}
|
||||
|
||||
static void init(struct plugin_conn *rpc)
|
||||
{
|
||||
const char *field;
|
||||
|
||||
field = rpc_delve(tmpctx, "getinfo", "", rpc, ".id");
|
||||
field = rpc_delve(tmpctx, "getinfo",
|
||||
take(json_out_obj(NULL, NULL, NULL)), rpc, ".id");
|
||||
if (!node_id_from_hexstr(field, strlen(field), &my_id))
|
||||
plugin_err("getinfo didn't contain valid id: '%s'", field);
|
||||
|
||||
field = rpc_delve(tmpctx, "listconfigs",
|
||||
"'config': 'max-locktime-blocks'",
|
||||
take(json_out_obj(NULL,
|
||||
"config", "max-locktime-blocks")),
|
||||
rpc, ".max-locktime-blocks");
|
||||
maxdelay_default = atoi(field);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user