mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 07:04:22 +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:
@@ -1,4 +1,5 @@
|
|||||||
#include <ccan/array_size/array_size.h>
|
#include <ccan/array_size/array_size.h>
|
||||||
|
#include <ccan/json_out/json_out.h>
|
||||||
#include <ccan/tal/str/str.h>
|
#include <ccan/tal/str/str.h>
|
||||||
#include <ccan/time/time.h>
|
#include <ccan/time/time.h>
|
||||||
#include <common/utils.h>
|
#include <common/utils.h>
|
||||||
@@ -22,10 +23,16 @@ static struct command_result *ignore(struct command *timer,
|
|||||||
|
|
||||||
static struct command_result *do_clean(void)
|
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! */
|
/* FIXME: delexpiredinvoice should be in our plugin too! */
|
||||||
return send_outreq(NULL, "delexpiredinvoice", ignore, ignore, NULL,
|
return send_outreq(NULL, "delexpiredinvoice", ignore, ignore, NULL,
|
||||||
"'maxexpirytime': %"PRIu64, age);
|
take(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct command_result *json_autocleaninvoice(struct command *cmd,
|
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) {
|
if (cycle_seconds == 0) {
|
||||||
tal_free(cleantimer);
|
tal_free(cleantimer);
|
||||||
return command_success(cmd, "'Autoclean timer disabled'");
|
return command_success_str(cmd, "Autoclean timer disabled");
|
||||||
}
|
}
|
||||||
tal_free(cleantimer);
|
tal_free(cleantimer);
|
||||||
cleantimer = plugin_timer(rpc, time_from_sec(cycle_seconds), do_clean);
|
cleantimer = plugin_timer(rpc, time_from_sec(cycle_seconds), do_clean);
|
||||||
|
|
||||||
return command_success(cmd, tal_fmt(cmd, "'Autocleaning %"PRIu64
|
return command_success_str(cmd,
|
||||||
"-second old invoices every %"PRIu64
|
tal_fmt(cmd, "Autocleaning %"PRIu64
|
||||||
" seconds'",
|
"-second old invoices every %"PRIu64
|
||||||
expired_by, cycle_seconds));
|
" seconds",
|
||||||
|
expired_by, cycle_seconds));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init(struct plugin_conn *prpc)
|
static void init(struct plugin_conn *prpc)
|
||||||
|
|||||||
@@ -84,6 +84,20 @@ struct command_result *command_param_failed(void)
|
|||||||
return &complete;
|
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 */
|
/* Realloc helper for tal membufs */
|
||||||
static void *membuf_tal_realloc(struct membuf *mb, void *rawelems,
|
static void *membuf_tal_realloc(struct membuf *mb, void *rawelems,
|
||||||
size_t newsize)
|
size_t newsize)
|
||||||
@@ -190,17 +204,7 @@ static struct command_result *WARN_UNUSED_RESULT end_cmd(struct command *cmd)
|
|||||||
return &complete;
|
return &complete;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: We promised callers we'd turn ' into ". */
|
/* str is raw JSON from RPC output. */
|
||||||
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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct command_result *WARN_UNUSED_RESULT
|
static struct command_result *WARN_UNUSED_RESULT
|
||||||
command_done_raw(struct command *cmd,
|
command_done_raw(struct command *cmd,
|
||||||
const char *label,
|
const char *label,
|
||||||
@@ -208,23 +212,42 @@ command_done_raw(struct command *cmd,
|
|||||||
{
|
{
|
||||||
struct json_out *jout = start_json_rpc(cmd, cmd->id);
|
struct json_out *jout = start_json_rpc(cmd, cmd->id);
|
||||||
|
|
||||||
copy_with_quote_sub(json_out_member_direct(jout, label, size),
|
memcpy(json_out_member_direct(jout, label, size), str, size);
|
||||||
str, size);
|
|
||||||
|
|
||||||
finish_and_send_json(STDOUT_FILENO, jout);
|
finish_and_send_json(STDOUT_FILENO, jout);
|
||||||
return end_cmd(cmd);
|
return end_cmd(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct command_result *WARN_UNUSED_RESULT
|
struct command_result *WARN_UNUSED_RESULT
|
||||||
command_done_ok(struct command *cmd, const char *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,
|
struct command_result *command_done_err(struct command *cmd,
|
||||||
int code,
|
int code,
|
||||||
const char *errmsg,
|
const char *errmsg,
|
||||||
const char *data)
|
const struct json_out *data)
|
||||||
{
|
{
|
||||||
struct json_out *jout = start_json_rpc(cmd, cmd->id);
|
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_add(jout, "code", false, "%d", code);
|
||||||
json_out_addstr(jout, "message", errmsg);
|
json_out_addstr(jout, "message", errmsg);
|
||||||
|
|
||||||
if (data) {
|
if (data)
|
||||||
char *p;
|
json_out_add_splice(jout, "data", data);
|
||||||
p = json_out_member_direct(jout, "data", strlen(data));
|
|
||||||
copy_with_quote_sub(p, data, strlen(data));
|
|
||||||
}
|
|
||||||
json_out_end(jout, '}');
|
json_out_end(jout, '}');
|
||||||
|
|
||||||
finish_and_send_json(STDOUT_FILENO, jout);
|
finish_and_send_json(STDOUT_FILENO, jout);
|
||||||
@@ -250,11 +270,6 @@ struct command_result *timer_complete(void)
|
|||||||
return &complete;
|
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,
|
struct command_result *forward_error(struct command *cmd,
|
||||||
const char *buf,
|
const char *buf,
|
||||||
const jsmntok_t *error,
|
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,
|
static struct json_out *start_json_request(const tal_t *ctx,
|
||||||
u64 id,
|
u64 id,
|
||||||
const char *method,
|
const char *method,
|
||||||
const char *params)
|
const struct json_out *params TAKES)
|
||||||
{
|
{
|
||||||
struct json_out *jout;
|
struct json_out *jout;
|
||||||
|
|
||||||
jout = start_json_rpc(tmpctx, id);
|
jout = start_json_rpc(tmpctx, id);
|
||||||
json_out_addstr(jout, "method", method);
|
json_out_addstr(jout, "method", method);
|
||||||
json_out_start(jout, "params", '{');
|
json_out_add_splice(jout, "params", params);
|
||||||
copy_with_quote_sub(json_out_direct(jout, strlen(params)),
|
if (taken(params))
|
||||||
params, strlen(params));
|
tal_free(params);
|
||||||
json_out_end(jout, '}');
|
|
||||||
|
|
||||||
return jout;
|
return jout;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Synchronous routine to send command and extract single field from response */
|
/* Synchronous routine to send command and extract single field from response */
|
||||||
const char *rpc_delve(const tal_t *ctx,
|
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)
|
struct plugin_conn *rpc, const char *guide)
|
||||||
{
|
{
|
||||||
bool error;
|
bool error;
|
||||||
@@ -435,12 +450,10 @@ send_outreq_(struct command *cmd,
|
|||||||
const jsmntok_t *result,
|
const jsmntok_t *result,
|
||||||
void *arg),
|
void *arg),
|
||||||
void *arg,
|
void *arg,
|
||||||
const char *paramfmt_single_ticks, ...)
|
const struct json_out *params TAKES)
|
||||||
{
|
{
|
||||||
va_list ap;
|
|
||||||
struct json_out *jout;
|
struct json_out *jout;
|
||||||
struct out_req *out;
|
struct out_req *out;
|
||||||
char *params;
|
|
||||||
|
|
||||||
out = tal(cmd, struct out_req);
|
out = tal(cmd, struct out_req);
|
||||||
out->id = next_outreq_id++;
|
out->id = next_outreq_id++;
|
||||||
@@ -450,10 +463,6 @@ send_outreq_(struct command *cmd,
|
|||||||
out->arg = arg;
|
out->arg = arg;
|
||||||
uintmap_add(&out_reqs, out->id, out);
|
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);
|
jout = start_json_request(tmpctx, out->id, method, params);
|
||||||
finish_and_send_json(rpc_conn.fd, jout);
|
finish_and_send_json(rpc_conn.fd, jout);
|
||||||
|
|
||||||
@@ -467,8 +476,6 @@ handle_getmanifest(struct command *getmanifest_cmd,
|
|||||||
const struct plugin_option *opts)
|
const struct plugin_option *opts)
|
||||||
{
|
{
|
||||||
struct json_out *params = json_out_new(tmpctx);
|
struct json_out *params = json_out_new(tmpctx);
|
||||||
size_t len;
|
|
||||||
const char *p;
|
|
||||||
|
|
||||||
json_out_start(params, NULL, '{');
|
json_out_start(params, NULL, '{');
|
||||||
json_out_start(params, "options", '[');
|
json_out_start(params, "options", '[');
|
||||||
@@ -497,8 +504,7 @@ handle_getmanifest(struct command *getmanifest_cmd,
|
|||||||
json_out_end(params, '}');
|
json_out_end(params, '}');
|
||||||
json_out_finished(params);
|
json_out_finished(params);
|
||||||
|
|
||||||
p = json_out_contents(params, &len);
|
return command_success(getmanifest_cmd, params);
|
||||||
return command_done_raw(getmanifest_cmd, "result", p, len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct command_result *handle_init(struct command *init_cmd,
|
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;
|
struct sockaddr_un addr;
|
||||||
size_t i;
|
size_t i;
|
||||||
char *dir;
|
char *dir;
|
||||||
|
struct json_out *param_obj;
|
||||||
|
|
||||||
/* Move into lightning directory: other files are relative */
|
/* Move into lightning directory: other files are relative */
|
||||||
dirtok = json_delve(buf, params, ".configuration.lightning-dir");
|
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,
|
rpctok->end - rpctok->start, buf + rpctok->start,
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
|
|
||||||
|
param_obj = json_out_obj(NULL, "config", "allow-deprecated-apis");
|
||||||
deprecated_apis = streq(rpc_delve(tmpctx, "listconfigs",
|
deprecated_apis = streq(rpc_delve(tmpctx, "listconfigs",
|
||||||
"'config': 'allow-deprecated-apis'",
|
take(param_obj),
|
||||||
&rpc_conn,
|
&rpc_conn,
|
||||||
".allow-deprecated-apis"),
|
".allow-deprecated-apis"),
|
||||||
"true");
|
"true");
|
||||||
@@ -558,7 +566,7 @@ static struct command_result *handle_init(struct command *init_cmd,
|
|||||||
if (init)
|
if (init)
|
||||||
init(&rpc_conn);
|
init(&rpc_conn);
|
||||||
|
|
||||||
return command_done_ok(init_cmd, "{}");
|
return command_success_str(init_cmd, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *u64_option(const char *arg, u64 *i)
|
char *u64_option(const char *arg, u64 *i)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <common/status_levels.h>
|
#include <common/status_levels.h>
|
||||||
|
|
||||||
struct command;
|
struct command;
|
||||||
|
struct json_out;
|
||||||
struct plugin_conn;
|
struct plugin_conn;
|
||||||
|
|
||||||
extern bool deprecated_apis;
|
extern bool deprecated_apis;
|
||||||
@@ -35,6 +36,12 @@ struct plugin_option {
|
|||||||
void *arg;
|
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. */
|
/* Return this iff the param() call failed in your handler. */
|
||||||
struct command_result *command_param_failed(void);
|
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, ...);
|
void NORETURN plugin_err(const char *fmt, ...);
|
||||||
|
|
||||||
/* This command is finished, here's a detailed error; @cmd cannot be
|
/* 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
|
struct command_result *WARN_UNUSED_RESULT
|
||||||
command_done_err(struct command *cmd,
|
command_done_err(struct command *cmd,
|
||||||
int code,
|
int code,
|
||||||
const char *errmsg,
|
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
|
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
|
/* Synchronous helper to send command and extract single field from
|
||||||
* response; can only be used in init callback. */
|
* response; can only be used in init callback. */
|
||||||
const char *rpc_delve(const tal_t *ctx,
|
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);
|
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.
|
* @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,
|
send_outreq_(struct command *cmd,
|
||||||
const char *method,
|
const char *method,
|
||||||
struct command_result *(*cb)(struct command *command,
|
struct command_result *(*cb)(struct command *command,
|
||||||
@@ -74,9 +88,9 @@ send_outreq_(struct command *cmd,
|
|||||||
const jsmntok_t *result,
|
const jsmntok_t *result,
|
||||||
void *arg),
|
void *arg),
|
||||||
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), \
|
send_outreq_((cmd), (method), \
|
||||||
typesafe_cb_preargs(struct command_result *, void *, \
|
typesafe_cb_preargs(struct command_result *, void *, \
|
||||||
(cb), (arg), \
|
(cb), (arg), \
|
||||||
@@ -88,7 +102,7 @@ send_outreq_(struct command *cmd,
|
|||||||
struct command *command, \
|
struct command *command, \
|
||||||
const char *buf, \
|
const char *buf, \
|
||||||
const jsmntok_t *result), \
|
const jsmntok_t *result), \
|
||||||
(arg), __VA_ARGS__)
|
(arg), (params))
|
||||||
|
|
||||||
/* Callback to just forward error and close request; @cmd cannot be NULL */
|
/* Callback to just forward error and close request; @cmd cannot be NULL */
|
||||||
struct command_result *forward_error(struct command *cmd,
|
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/array_size/array_size.h>
|
||||||
#include <ccan/cast/cast.h>
|
#include <ccan/cast/cast.h>
|
||||||
#include <ccan/intmap/intmap.h>
|
#include <ccan/intmap/intmap.h>
|
||||||
|
#include <ccan/json_out/json_out.h>
|
||||||
#include <ccan/tal/str/str.h>
|
#include <ccan/tal/str/str.h>
|
||||||
#include <ccan/time/time.h>
|
#include <ccan/time/time.h>
|
||||||
#include <common/amount.h>
|
#include <common/amount.h>
|
||||||
@@ -136,12 +137,38 @@ static void attempt_failed_tok(struct pay_command *pc, const char *method,
|
|||||||
buf + msg->start);
|
buf + msg->start);
|
||||||
else
|
else
|
||||||
attempt_failed_fmt(pc,
|
attempt_failed_fmt(pc,
|
||||||
"{ 'message': 'Call to %s failed', %.*s",
|
"{ \"message\": \"Call to %s failed\", %.*s",
|
||||||
method,
|
method,
|
||||||
errtok->end - errtok->start - 1,
|
errtok->end - errtok->start - 1,
|
||||||
buf + 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,
|
static struct command_result *start_pay_attempt(struct command *cmd,
|
||||||
struct pay_command *pc,
|
struct pay_command *pc,
|
||||||
const char *fmt, ...);
|
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,
|
static struct command_result *waitsendpay_expired(struct command *cmd,
|
||||||
struct pay_command *pc)
|
struct pay_command *pc)
|
||||||
{
|
{
|
||||||
char *errmsg, *data;
|
char *errmsg;
|
||||||
|
struct json_out *data;
|
||||||
size_t num_attempts = count_sendpays(pc->ps->attempts);
|
size_t num_attempts = count_sendpays(pc->ps->attempts);
|
||||||
|
|
||||||
errmsg = tal_fmt(pc, "Gave up after %zu attempt%s: see paystatus",
|
errmsg = tal_fmt(pc, "Gave up after %zu attempt%s: see paystatus",
|
||||||
num_attempts, num_attempts == 1 ? "" : "s");
|
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++) {
|
for (size_t i = 0; i < tal_count(pc->ps->attempts); i++) {
|
||||||
if (pc->ps->attempts[i].route)
|
if (pc->ps->attempts[i].route)
|
||||||
tal_append_fmt(&data, "%s { 'route': %s,\n 'failure': %s\n }",
|
json_out_add_raw(data, "route",
|
||||||
i == 0 ? "" : ",",
|
pc->ps->attempts[i].route);
|
||||||
pc->ps->attempts[i].route,
|
json_out_add_raw(data, "failure", pc->ps->attempts[i].failure);
|
||||||
pc->ps->attempts[i].failure);
|
|
||||||
else
|
|
||||||
tal_append_fmt(&data, "%s { 'failure': %s\n }",
|
|
||||||
i == 0 ? "" : ",",
|
|
||||||
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);
|
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",
|
return send_outreq(cmd, "waitsendpay",
|
||||||
waitsendpay_done, waitsendpay_error, pc,
|
waitsendpay_done, waitsendpay_error, pc,
|
||||||
"'payment_hash': '%s'",
|
take(json_out_obj(NULL, "payment_hash",
|
||||||
pc->payment_hash);
|
pc->payment_hash)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate how many millisatoshi we need at the start of this route
|
/* 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);
|
return tal_free(ret);
|
||||||
|
|
||||||
tal_append_fmt(&ret, ", {"
|
tal_append_fmt(&ret, ", {"
|
||||||
" 'id': '%s',"
|
" \"id\": \"%s\","
|
||||||
" 'channel': '%s',"
|
" \"channel\": \"%s\","
|
||||||
" 'msatoshi': '%s',"
|
" \"msatoshi\": \"%s\","
|
||||||
" 'delay': %u }",
|
" \"delay\": %u }",
|
||||||
/* pubkey of *destination* */
|
/* pubkey of *destination* */
|
||||||
route_pubkey(tmpctx, pc, routehint, i + 1),
|
route_pubkey(tmpctx, pc, routehint, i + 1),
|
||||||
type_to_string(tmpctx, struct short_channel_id,
|
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);
|
struct pay_attempt *attempt = current_attempt(pc);
|
||||||
const jsmntok_t *t = json_get_member(buf, result, "route");
|
const jsmntok_t *t = json_get_member(buf, result, "route");
|
||||||
char *json_desc;
|
|
||||||
struct amount_msat fee;
|
struct amount_msat fee;
|
||||||
u32 delay;
|
u32 delay;
|
||||||
double feepercent;
|
double feepercent;
|
||||||
|
struct json_out *params;
|
||||||
|
|
||||||
if (!t)
|
if (!t)
|
||||||
plugin_err("getroute gave no 'route'? '%.*s'",
|
plugin_err("getroute gave no 'route'? '%.*s'",
|
||||||
@@ -469,7 +495,7 @@ static struct command_result *getroute_done(struct command *cmd,
|
|||||||
pc, pc->current_routehint);
|
pc, pc->current_routehint);
|
||||||
if (!attempt->route) {
|
if (!attempt->route) {
|
||||||
attempt_failed_fmt(pc,
|
attempt_failed_fmt(pc,
|
||||||
"{ 'message': 'Joining routehint gave absurd fee' }");
|
"{ \"message\": \"Joining routehint gave absurd fee\" }");
|
||||||
return next_routehint(cmd, pc);
|
return next_routehint(cmd, pc);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@@ -497,7 +523,7 @@ static struct command_result *getroute_done(struct command *cmd,
|
|||||||
&& feepercent > pc->maxfeepercent) {
|
&& feepercent > pc->maxfeepercent) {
|
||||||
const jsmntok_t *charger;
|
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,
|
type_to_string(tmpctx, struct amount_msat,
|
||||||
&fee));
|
&fee));
|
||||||
|
|
||||||
@@ -526,7 +552,7 @@ static struct command_result *getroute_done(struct command *cmd,
|
|||||||
const jsmntok_t *delayer;
|
const jsmntok_t *delayer;
|
||||||
|
|
||||||
attempt_failed_fmt(pc,
|
attempt_failed_fmt(pc,
|
||||||
"{ 'message': 'Route wanted delay of %u blocks' }",
|
"{ \"message\": \"Route wanted delay of %u blocks\" }",
|
||||||
delay);
|
delay);
|
||||||
|
|
||||||
/* Remember this if we eliminating this causes us to have no
|
/* 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);
|
return next_routehint(cmd, pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pc->label)
|
|
||||||
json_desc = tal_fmt(pc, ", 'label': '%s'", pc->label);
|
|
||||||
else
|
|
||||||
json_desc = "";
|
|
||||||
|
|
||||||
attempt->sendpay = true;
|
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,
|
return send_outreq(cmd, "sendpay", sendpay_done, sendpay_error, pc,
|
||||||
"'route': %s, 'payment_hash': '%s', 'bolt11': '%s'%s",
|
take(params));
|
||||||
attempt->route,
|
|
||||||
pc->payment_hash,
|
|
||||||
pc->ps->bolt11,
|
|
||||||
json_desc);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,7 +626,6 @@ static struct command_result *start_pay_attempt(struct command *cmd,
|
|||||||
struct pay_command *pc,
|
struct pay_command *pc,
|
||||||
const char *fmt, ...)
|
const char *fmt, ...)
|
||||||
{
|
{
|
||||||
char *exclude;
|
|
||||||
struct amount_msat msat;
|
struct amount_msat msat;
|
||||||
const char *dest;
|
const char *dest;
|
||||||
size_t max_hops = ROUTING_MAX_HOPS;
|
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;
|
struct pay_attempt *attempt;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
size_t n;
|
size_t n;
|
||||||
|
struct json_out *params;
|
||||||
|
|
||||||
n = tal_count(pc->ps->attempts);
|
n = tal_count(pc->ps->attempts);
|
||||||
tal_resize(&pc->ps->attempts, n+1);
|
tal_resize(&pc->ps->attempts, n+1);
|
||||||
@@ -627,17 +653,6 @@ static struct command_result *start_pay_attempt(struct command *cmd,
|
|||||||
|
|
||||||
/* routehint set below. */
|
/* 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
|
/* If we have a routehint, try that first; we need to do extra
|
||||||
* checks that it meets our criteria though. */
|
* checks that it meets our criteria though. */
|
||||||
if (pc->current_routehint) {
|
if (pc->current_routehint) {
|
||||||
@@ -646,7 +661,7 @@ static struct command_result *start_pay_attempt(struct command *cmd,
|
|||||||
attempt->routehint,
|
attempt->routehint,
|
||||||
tal_count(attempt->routehint))) {
|
tal_count(attempt->routehint))) {
|
||||||
attempt_failed_fmt(pc,
|
attempt_failed_fmt(pc,
|
||||||
"{ 'message': 'Routehint absurd fee' }");
|
"{ \"message\": \"Routehint absurd fee\" }");
|
||||||
return next_routehint(cmd, pc);
|
return next_routehint(cmd, pc);
|
||||||
}
|
}
|
||||||
dest = type_to_string(tmpctx, struct node_id,
|
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 */
|
/* 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,
|
return send_outreq(cmd, "getroute", getroute_done, getroute_error, pc,
|
||||||
"'id': '%s',"
|
take(params));
|
||||||
"'msatoshi': '%s',"
|
|
||||||
"'cltv': %u,"
|
|
||||||
"'maxhops': %zu,"
|
|
||||||
"'riskfactor': %f%s",
|
|
||||||
dest,
|
|
||||||
type_to_string(tmpctx, struct amount_msat, &msat),
|
|
||||||
cltv, max_hops, pc->riskfactor, exclude);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BOLT #7:
|
/* BOLT #7:
|
||||||
@@ -751,7 +775,7 @@ static struct command_result *shadow_route(struct command *cmd,
|
|||||||
|
|
||||||
return send_outreq(cmd, "listchannels",
|
return send_outreq(cmd, "listchannels",
|
||||||
add_shadow_route, forward_error, pc,
|
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
|
/* 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->routehints = filter_routehints(pc, b11->routes);
|
||||||
pc->expensive_route = NULL;
|
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,
|
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? */
|
/* 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);
|
(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_status *ps,
|
||||||
const struct pay_attempt *attempt)
|
const struct pay_attempt *attempt)
|
||||||
{
|
{
|
||||||
@@ -1022,62 +1045,56 @@ static void add_attempt(char **ret,
|
|||||||
|
|
||||||
utc_timestring(&attempt->start, timestr);
|
utc_timestring(&attempt->start, timestr);
|
||||||
|
|
||||||
tal_append_fmt(ret, "{ 'strategy': '%s',"
|
json_out_start(ret, NULL, '{');
|
||||||
" 'start_time': '%s',"
|
json_out_addstr(ret, "strategy", attempt->why);
|
||||||
" 'age_in_seconds': %"PRIu64,
|
json_out_addstr(ret, "start_time", timestr);
|
||||||
attempt->why,
|
json_out_add_u64(ret, "age_in_seconds",
|
||||||
timestr,
|
time_to_sec(time_between(time_now(), attempt->start)));
|
||||||
time_to_sec(time_between(time_now(), attempt->start)));
|
|
||||||
if (attempt->result || attempt->failure) {
|
if (attempt->result || attempt->failure) {
|
||||||
utc_timestring(&attempt->end, timestr);
|
utc_timestring(&attempt->end, timestr);
|
||||||
tal_append_fmt(ret, ", 'end_time': '%s'"
|
json_out_addstr(ret, "end_time", timestr);
|
||||||
", 'duration_in_seconds': %"PRIu64,
|
json_out_add_u64(ret, "duration_in_seconds",
|
||||||
timestr,
|
time_to_sec(time_between(attempt->end,
|
||||||
time_to_sec(time_between(attempt->end,
|
attempt->start)));
|
||||||
attempt->start)));
|
|
||||||
}
|
}
|
||||||
if (tal_count(attempt->routehint)) {
|
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++) {
|
for (size_t i = 0; i < tal_count(attempt->routehint); i++) {
|
||||||
tal_append_fmt(ret, "%s{"
|
json_out_start(ret, NULL, '{');
|
||||||
" 'id': '%s',"
|
json_out_addstr(ret, "id",
|
||||||
" 'channel': '%s',"
|
type_to_string(tmpctx, struct node_id,
|
||||||
" 'fee_base_msat': %u,"
|
&attempt->routehint[i].pubkey));
|
||||||
" 'fee_proportional_millionths': %u,"
|
json_out_addstr(ret, "channel",
|
||||||
" 'cltv_expiry_delta': %u }",
|
type_to_string(tmpctx,
|
||||||
i == 0 ? "" : ", ",
|
struct short_channel_id,
|
||||||
type_to_string(tmpctx, struct node_id,
|
&attempt->routehint[i].short_channel_id));
|
||||||
&attempt->routehint[i].pubkey),
|
json_out_add_u64(ret, "fee_base_msat",
|
||||||
type_to_string(tmpctx,
|
attempt->routehint[i].fee_base_msat);
|
||||||
struct short_channel_id,
|
json_out_add_u64(ret, "fee_proportional_millionths",
|
||||||
&attempt->routehint[i].short_channel_id),
|
attempt->routehint[i].fee_proportional_millionths);
|
||||||
attempt->routehint[i].fee_base_msat,
|
json_out_add_u64(ret, "cltv_expiry_delta",
|
||||||
attempt->routehint[i].fee_proportional_millionths,
|
attempt->routehint[i].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)) {
|
if (tal_count(attempt->excludes)) {
|
||||||
for (size_t i = 0; i < tal_count(attempt->excludes); i++) {
|
json_out_start(ret, "excluded_channels", '[');
|
||||||
if (i == 0)
|
for (size_t i = 0; i < tal_count(attempt->excludes); i++)
|
||||||
tal_append_fmt(ret, ", 'excluded_channels': [");
|
json_out_addstr(ret, NULL, attempt->excludes[i]);
|
||||||
else
|
json_out_end(ret, ']');
|
||||||
tal_append_fmt(ret, ", ");
|
|
||||||
tal_append_fmt(ret, "'%s'", attempt->excludes[i]);
|
|
||||||
}
|
|
||||||
tal_append_fmt(ret, "]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attempt->route)
|
if (attempt->route)
|
||||||
tal_append_fmt(ret, ", 'route': %s", attempt->route);
|
json_out_add_raw(ret, "route", attempt->route);
|
||||||
|
|
||||||
if (attempt->failure)
|
if (attempt->failure)
|
||||||
tal_append_fmt(ret, ", 'failure': %s", attempt->failure);
|
json_out_add_raw(ret, "failure", attempt->failure);
|
||||||
|
|
||||||
if (attempt->result)
|
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,
|
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;
|
struct pay_status *ps;
|
||||||
const char *b11str;
|
const char *b11str;
|
||||||
char *ret;
|
struct json_out *ret;
|
||||||
bool some = false;
|
|
||||||
|
|
||||||
if (!param(cmd, buf, params,
|
if (!param(cmd, buf, params,
|
||||||
p_opt("bolt11", param_string, &b11str),
|
p_opt("bolt11", param_string, &b11str),
|
||||||
NULL))
|
NULL))
|
||||||
return 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! */
|
/* FIXME: Index by bolt11 string! */
|
||||||
list_for_each(&pay_status, ps, list) {
|
list_for_each(&pay_status, ps, list) {
|
||||||
if (b11str && !streq(b11str, ps->bolt11))
|
if (b11str && !streq(b11str, ps->bolt11))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (some)
|
json_out_start(ret, NULL, '{');
|
||||||
tal_append_fmt(&ret, ",\n");
|
json_out_addstr(ret, "bolt11", ps->bolt11);
|
||||||
some = true;
|
json_out_add_u64(ret, "msatoshi",
|
||||||
|
ps->msat.millisatoshis); /* Raw: JSON */
|
||||||
tal_append_fmt(&ret, "{ 'bolt11': '%s',"
|
json_out_addstr(ret, "amount_msat",
|
||||||
" 'msatoshi': %"PRIu64", "
|
|
||||||
" 'amount_msat': '%s', "
|
|
||||||
" 'destination': '%s'",
|
|
||||||
ps->bolt11,
|
|
||||||
ps->msat.millisatoshis, /* Raw: JSON */
|
|
||||||
type_to_string(tmpctx, struct 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)
|
if (ps->label)
|
||||||
tal_append_fmt(&ret, ", 'label': '%s'", ps->label);
|
json_out_addstr(ret, "label", ps->label);
|
||||||
if (ps->routehint_modifications)
|
if (ps->routehint_modifications)
|
||||||
tal_append_fmt(&ret, ", 'routehint_modifications': '%s'",
|
json_out_addstr(ret, "routehint_modifications",
|
||||||
ps->routehint_modifications);
|
ps->routehint_modifications);
|
||||||
if (ps->shadow && !streq(ps->shadow, ""))
|
if (ps->shadow && !streq(ps->shadow, ""))
|
||||||
tal_append_fmt(&ret, ", 'shadow': '%s'", ps->shadow);
|
json_out_addstr(ret, "shadow", ps->shadow);
|
||||||
if (ps->exclusions)
|
if (ps->exclusions)
|
||||||
tal_append_fmt(&ret, ", 'local_exclusions': '%s'",
|
json_out_addstr(ret, "local_exclusions", ps->exclusions);
|
||||||
ps->exclusions);
|
|
||||||
|
|
||||||
assert(tal_count(ps->attempts));
|
assert(tal_count(ps->attempts));
|
||||||
for (size_t i = 0; i < tal_count(ps->attempts); i++) {
|
json_out_start(ret, "attempts", '[');
|
||||||
if (i == 0)
|
for (size_t i = 0; i < tal_count(ps->attempts); i++)
|
||||||
tal_append_fmt(&ret, ", 'attempts': [");
|
add_attempt(ret, ps, &ps->attempts[i]);
|
||||||
else
|
json_out_end(ret, ']');
|
||||||
tal_append_fmt(&ret, ",");
|
json_out_end(ret, '}');
|
||||||
|
|
||||||
add_attempt(&ret, ps, &ps->attempts[i]);
|
|
||||||
}
|
|
||||||
tal_append_fmt(&ret, "] }");
|
|
||||||
}
|
}
|
||||||
tal_append_fmt(&ret, "] }");
|
json_out_end(ret, ']');
|
||||||
|
json_out_end(ret, '}');
|
||||||
|
|
||||||
return command_success(cmd, ret);
|
return command_success(cmd, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const jsmntok_t *copy_member(char **ret,
|
/* Copy field and member to output, if it exists: return member */
|
||||||
const char *buf,
|
static const jsmntok_t *copy_member(struct json_out *ret,
|
||||||
const jsmntok_t *result,
|
const char *buf, const jsmntok_t *obj,
|
||||||
const char *membername,
|
const char *membername)
|
||||||
const char *term)
|
|
||||||
{
|
{
|
||||||
const jsmntok_t *m = json_get_member(buf, result, membername);
|
const jsmntok_t *m = json_get_member(buf, obj, membername);
|
||||||
if (m)
|
if (!m)
|
||||||
tal_append_fmt(ret, "'%s': %.*s%s",
|
return NULL;
|
||||||
membername,
|
|
||||||
json_tok_full_len(m), json_tok_full(buf, m),
|
/* Literal copy: it's already JSON escaped, and may be a string. */
|
||||||
term);
|
json_out_add_raw_len(ret, membername,
|
||||||
|
json_tok_full(buf, m), json_tok_full_len(m));
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1175,33 +1186,30 @@ static struct command_result *listsendpays_done(struct command *cmd,
|
|||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
const jsmntok_t *t, *arr;
|
const jsmntok_t *t, *arr;
|
||||||
char *ret;
|
struct json_out *ret;
|
||||||
bool some = false;
|
|
||||||
|
|
||||||
arr = json_get_member(buf, result, "payments");
|
arr = json_get_member(buf, result, "payments");
|
||||||
if (!arr || arr->type != JSMN_ARRAY)
|
if (!arr || arr->type != JSMN_ARRAY)
|
||||||
return command_fail(cmd, LIGHTNINGD,
|
return command_fail(cmd, LIGHTNINGD,
|
||||||
"Unexpected non-array result from listsendpays");
|
"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) {
|
json_for_each_arr(i, t, arr) {
|
||||||
const jsmntok_t *status, *b11;
|
const jsmntok_t *status, *b11;
|
||||||
|
|
||||||
if (some)
|
json_out_start(ret, NULL, '{');
|
||||||
tal_append_fmt(&ret, ",\n");
|
|
||||||
some = true;
|
|
||||||
|
|
||||||
tal_append_fmt(&ret, "{");
|
|
||||||
/* Old payments didn't have bolt11 field */
|
/* Old payments didn't have bolt11 field */
|
||||||
b11 = copy_member(&ret, buf, t, "bolt11", ",");
|
b11 = copy_member(ret, buf, t, "bolt11");
|
||||||
if (!b11) {
|
if (!b11) {
|
||||||
if (b11str) {
|
if (b11str) {
|
||||||
/* If it's a single query, we can fake it */
|
/* If it's a single query, we can fake it */
|
||||||
tal_append_fmt(&ret, "'bolt11': '%s',", b11str);
|
json_out_addstr(ret, "bolt11", b11str);
|
||||||
} else {
|
} else {
|
||||||
copy_member(&ret, buf, t, "payment_hash", ",");
|
copy_member(ret, buf, t, "payment_hash");
|
||||||
copy_member(&ret, buf, t, "destination", ",");
|
copy_member(ret, buf, t, "destination");
|
||||||
copy_member(&ret, buf, t, "amount_msat", ",");
|
copy_member(ret, buf, t, "amount_msat");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1210,19 +1218,20 @@ static struct command_result *listsendpays_done(struct command *cmd,
|
|||||||
if (status) {
|
if (status) {
|
||||||
if (json_tok_streq(buf, status, "failed")
|
if (json_tok_streq(buf, status, "failed")
|
||||||
&& attempt_ongoing(buf, b11)) {
|
&& attempt_ongoing(buf, b11)) {
|
||||||
tal_append_fmt(&ret, "'status': 'pending',");
|
json_out_addstr(ret, "status", "pending");
|
||||||
} else {
|
} else {
|
||||||
copy_member(&ret, buf, t, "status", ",");
|
copy_member(ret, buf, t, "status");
|
||||||
if (json_tok_streq(buf, status, "complete"))
|
if (json_tok_streq(buf, status, "complete"))
|
||||||
copy_member(&ret, buf, t,
|
copy_member(ret, buf, t,
|
||||||
"payment_preimage", ",");
|
"payment_preimage");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
copy_member(&ret, buf, t, "label", ",");
|
copy_member(ret, buf, t, "label");
|
||||||
copy_member(&ret, buf, t, "amount_sent_msat", "");
|
copy_member(ret, buf, t, "amount_sent_msat");
|
||||||
tal_append_fmt(&ret, "}");
|
json_out_end(ret, '}');
|
||||||
}
|
}
|
||||||
tal_append_fmt(&ret, "] }");
|
json_out_end(ret, ']');
|
||||||
|
json_out_end(ret, '}');
|
||||||
return command_success(cmd, ret);
|
return command_success(cmd, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1230,7 +1239,7 @@ static struct command_result *json_listpays(struct command *cmd,
|
|||||||
const char *buf,
|
const char *buf,
|
||||||
const jsmntok_t *params)
|
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 */
|
/* FIXME: would be nice to parse as a bolt11 so check worked in future */
|
||||||
if (!param(cmd, buf, params,
|
if (!param(cmd, buf, params,
|
||||||
@@ -1238,26 +1247,25 @@ static struct command_result *json_listpays(struct command *cmd,
|
|||||||
NULL))
|
NULL))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (b11str)
|
|
||||||
paramstr = tal_fmt(tmpctx, "'bolt11' : '%s'", b11str);
|
|
||||||
else
|
|
||||||
paramstr = "";
|
|
||||||
return send_outreq(cmd, "listsendpays",
|
return send_outreq(cmd, "listsendpays",
|
||||||
listsendpays_done, forward_error,
|
listsendpays_done, forward_error,
|
||||||
cast_const(char *, b11str),
|
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)
|
static void init(struct plugin_conn *rpc)
|
||||||
{
|
{
|
||||||
const char *field;
|
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))
|
if (!node_id_from_hexstr(field, strlen(field), &my_id))
|
||||||
plugin_err("getinfo didn't contain valid id: '%s'", field);
|
plugin_err("getinfo didn't contain valid id: '%s'", field);
|
||||||
|
|
||||||
field = rpc_delve(tmpctx, "listconfigs",
|
field = rpc_delve(tmpctx, "listconfigs",
|
||||||
"'config': 'max-locktime-blocks'",
|
take(json_out_obj(NULL,
|
||||||
|
"config", "max-locktime-blocks")),
|
||||||
rpc, ".max-locktime-blocks");
|
rpc, ".max-locktime-blocks");
|
||||||
maxdelay_default = atoi(field);
|
maxdelay_default = atoi(field);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user