From e46ce0fc84001ea1860f7aa6e52947b5978970b1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 19 Oct 2018 11:47:48 +1030 Subject: [PATCH] jsonrpc: declare up front whether a response is success or fail. Such an API is required for when we stream it directly. Almost all our handlers fit this pattern already, or nearly do. We remove new_json_result() in favor of explicit json_stream_success() and json_stream_fail(), but still allowing command_fail() if you just want a simple all-in-one fail wrapper. Signed-off-by: Rusty Russell --- lightningd/chaintopology.c | 2 +- lightningd/connect_control.c | 2 +- lightningd/gossip_control.c | 14 ++-- lightningd/invoice.c | 48 ++++++------- lightningd/json.c | 24 ++++++- lightningd/json.h | 22 +++++- lightningd/jsonrpc.c | 44 ++++++------ lightningd/jsonrpc.h | 13 ++-- lightningd/log.c | 3 +- lightningd/memdump.c | 6 +- lightningd/opening_control.c | 2 +- lightningd/options.c | 18 +++-- lightningd/pay.c | 49 ++++++------- lightningd/payalgo.c | 80 ++++++++++----------- lightningd/peer_control.c | 10 +-- lightningd/ping.c | 2 +- lightningd/test/run-invoice-select-inchan.c | 22 +++--- lightningd/test/run-jsonrpc.c | 10 +-- lightningd/test/run-param.c | 12 ---- wallet/test/run-wallet.c | 8 +-- wallet/walletrpc.c | 28 ++++---- 21 files changed, 224 insertions(+), 195 deletions(-) diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index 76e089af5..2dc7069b0 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -483,7 +483,7 @@ static void json_feerates(struct command *cmd, missing = true; } - response = new_json_result(cmd); + response = json_stream_success(cmd); json_object_start(response, NULL); json_object_start(response, json_feerate_style_name(*style)); for (size_t i = 0; i < ARRAY_SIZE(feerates); i++) { diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 676a607be..128605326 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -65,7 +65,7 @@ static struct connect *find_connect(struct lightningd *ld, static void connect_cmd_succeed(struct command *cmd, const struct pubkey *id) { - struct json_result *response = new_json_result(cmd); + struct json_result *response = json_stream_success(cmd); json_object_start(response, NULL); json_add_pubkey(response, "id", id); json_object_end(response); diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index fa405d6b0..d4e8f375f 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -182,7 +182,7 @@ static void json_getnodes_reply(struct subd *gossip UNUSED, const u8 *reply, struct command *cmd) { struct gossip_getnodes_entry **nodes; - struct json_result *response = new_json_result(cmd); + struct json_result *response; size_t i, j; if (!fromwire_gossip_getnodes_reply(reply, reply, &nodes)) { @@ -190,6 +190,7 @@ static void json_getnodes_reply(struct subd *gossip UNUSED, const u8 *reply, return; } + response = json_stream_success(cmd); json_object_start(response, NULL); json_array_start(response, "nodes"); @@ -264,7 +265,7 @@ static void json_getroute_reply(struct subd *gossip UNUSED, const u8 *reply, con return; } - response = new_json_result(cmd); + response = json_stream_success(cmd); json_object_start(response, NULL); json_add_route(response, "route", hops, tal_count(hops)); json_object_end(response); @@ -336,13 +337,14 @@ static void json_listchannels_reply(struct subd *gossip UNUSED, const u8 *reply, { size_t i; struct gossip_getchannels_entry *entries; - struct json_result *response = new_json_result(cmd); + struct json_result *response; if (!fromwire_gossip_getchannels_reply(reply, reply, &entries)) { command_fail(cmd, LIGHTNINGD, "Invalid reply from gossipd"); return; } + response = json_stream_success(cmd); json_object_start(response, NULL); json_array_start(response, "channels"); for (i = 0; i < tal_count(entries); i++) { @@ -405,7 +407,7 @@ static void json_scids_reply(struct subd *gossip UNUSED, const u8 *reply, const int *fds UNUSED, struct command *cmd) { bool ok, complete; - struct json_result *response = new_json_result(cmd); + struct json_result *response; if (!fromwire_gossip_scids_reply(reply, &ok, &complete)) { command_fail(cmd, LIGHTNINGD, @@ -419,6 +421,7 @@ static void json_scids_reply(struct subd *gossip UNUSED, const u8 *reply, return; } + response = json_stream_success(cmd); json_object_start(response, NULL); json_add_bool(response, "complete", complete); json_object_end(response); @@ -500,7 +503,7 @@ AUTODATA(json_command, &dev_send_timestamp_filter); static void json_channel_range_reply(struct subd *gossip UNUSED, const u8 *reply, const int *fds UNUSED, struct command *cmd) { - struct json_result *response = new_json_result(cmd); + struct json_result *response; u32 final_first_block, final_num_blocks; bool final_complete; struct short_channel_id *scids; @@ -521,6 +524,7 @@ static void json_channel_range_reply(struct subd *gossip UNUSED, const u8 *reply return; } + response = json_stream_success(cmd); json_object_start(response, NULL); /* As this is a dev interface, we don't bother saving and * returning all the replies, just the final one. */ diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 18d223e03..09e3f3c79 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -64,19 +64,23 @@ static void json_add_invoice(struct json_result *response, static void tell_waiter(struct command *cmd, const struct invoice *inv) { - struct json_result *response = new_json_result(cmd); + struct json_result *response; const struct invoice_details *details; details = wallet_invoice_details(cmd, cmd->ld->wallet, *inv); - json_add_invoice(response, details); - if (details->state == PAID) + if (details->state == PAID) { + response = json_stream_success(cmd); + json_add_invoice(response, details); command_success(cmd, response); - else { + } else { /* FIXME: -2 should be a constant in jsonrpc_errors.h. */ - command_fail_detailed(cmd, -2, response, - "invoice expired during wait"); + response = json_stream_fail(cmd, -2, + "invoice expired during wait"); + json_add_invoice(response, details); + command_failed(cmd, response); } } + static void tell_waiter_deleted(struct command *cmd) { command_fail(cmd, LIGHTNINGD, "Invoice deleted during wait"); @@ -209,7 +213,7 @@ static void gossipd_incoming_channels_reply(struct subd *gossipd, const int *fs, struct invoice_info *info) { - struct json_result *response = new_json_result(info->cmd); + struct json_result *response; struct route_info *inchans; bool any_offline; struct invoice invoice; @@ -257,6 +261,7 @@ static void gossipd_incoming_channels_reply(struct subd *gossipd, /* Get details */ details = wallet_invoice_details(info, wallet, invoice); + response = json_stream_success(info->cmd); json_object_start(response, NULL); json_add_hex(response, "payment_hash", details->rhash.u.u8, sizeof(details->rhash)); @@ -413,12 +418,13 @@ static void json_listinvoices(struct command *cmd, const char *buffer, const jsmntok_t *params) { struct json_escaped *label; - struct json_result *response = new_json_result(cmd); + struct json_result *response; struct wallet *wallet = cmd->ld->wallet; if (!param(cmd, buffer, params, p_opt("label", json_tok_label, &label), NULL)) return; + response = json_stream_success(cmd); json_object_start(response, NULL); json_array_start(response, "invoices"); json_add_invoices(response, wallet, label); @@ -439,7 +445,7 @@ static void json_delinvoice(struct command *cmd, { struct invoice i; const struct invoice_details *details; - struct json_result *response = new_json_result(cmd); + struct json_result *response; const char *status, *actual_status; struct json_escaped *label; struct wallet *wallet = cmd->ld->wallet; @@ -466,10 +472,6 @@ static void json_delinvoice(struct command *cmd, return; } - /* Get invoice details before attempting to delete, as - * otherwise the invoice will be freed. */ - json_add_invoice(response, details); - if (!wallet_invoice_delete(wallet, i)) { log_broken(cmd->ld->log, "Error attempting to remove invoice %"PRIu64, @@ -478,6 +480,8 @@ static void json_delinvoice(struct command *cmd, return; } + response = json_stream_success(cmd); + json_add_invoice(response, details); command_success(cmd, response); } @@ -492,7 +496,6 @@ static void json_delexpiredinvoice(struct command *cmd, const char *buffer, const jsmntok_t *params) { u64 *maxexpirytime; - struct json_result *result; if (!param(cmd, buffer, params, p_opt_def("maxexpirytime", json_tok_u64, &maxexpirytime, @@ -502,10 +505,7 @@ static void json_delexpiredinvoice(struct command *cmd, const char *buffer, wallet_invoice_delete_expired(cmd->ld->wallet, *maxexpirytime); - result = new_json_result(cmd); - json_object_start(result, NULL); - json_object_end(result); - command_success(cmd, result); + command_success(cmd, null_response(cmd)); } static const struct json_command delexpiredinvoice_command = { "delexpiredinvoice", @@ -520,7 +520,6 @@ static void json_autocleaninvoice(struct command *cmd, { u64 *cycle; u64 *exby; - struct json_result *result; if (!param(cmd, buffer, params, p_opt_def("cycle_seconds", json_tok_u64, &cycle, 3600), @@ -530,10 +529,7 @@ static void json_autocleaninvoice(struct command *cmd, wallet_invoice_autoclean(cmd->ld->wallet, *cycle, *exby); - result = new_json_result(cmd); - json_object_start(result, NULL); - json_object_end(result); - command_success(cmd, result); + command_success(cmd, null_response(cmd)); } static const struct json_command autocleaninvoice_command = { "autocleaninvoice", @@ -656,8 +652,8 @@ static void json_decodepay(struct command *cmd, { struct bolt11 *b11; struct json_result *response; - const char *str, *desc; - char *fail; + const char *str, *desc; + char *fail; if (!param(cmd, buffer, params, p_req("bolt11", json_tok_string, &str), @@ -672,7 +668,7 @@ static void json_decodepay(struct command *cmd, return; } - response = new_json_result(cmd); + response = json_stream_success(cmd); json_object_start(response, NULL); json_add_string(response, "currency", b11->chain->bip173_name); diff --git a/lightningd/json.c b/lightningd/json.c index afa725c37..3ea5417de 100644 --- a/lightningd/json.c +++ b/lightningd/json.c @@ -703,9 +703,9 @@ void json_add_escaped_string(struct json_result *result, const char *fieldname, tal_free(esc); } -struct json_result *new_json_result(const tal_t *ctx) +static struct json_result *new_json_stream(struct command *cmd) { - struct json_result *r = tal(ctx, struct json_result); + struct json_result *r = tal(cmd, struct json_result); r->s = tal_strdup(r, ""); #if DEVELOPER @@ -713,9 +713,29 @@ struct json_result *new_json_result(const tal_t *ctx) #endif r->indent = 0; r->empty = true; + + assert(!cmd->have_json_stream); + cmd->have_json_stream = true; return r; } +struct json_result *json_stream_success(struct command *cmd) +{ + cmd->failcode = 0; + return new_json_stream(cmd); +} + +struct json_result *json_stream_fail(struct command *cmd, + int code, + const char *errmsg) +{ + assert(code); + assert(errmsg); + cmd->failcode = code; + cmd->errmsg = tal_strdup(cmd, errmsg); + return new_json_stream(cmd); +} + const char *json_result_string(const struct json_result *result) { #if DEVELOPER diff --git a/lightningd/json.h b/lightningd/json.h index ed693b45e..66135b3a0 100644 --- a/lightningd/json.h +++ b/lightningd/json.h @@ -170,7 +170,27 @@ void json_array_end(struct json_result *ptr); /* ' }, ' */ void json_object_end(struct json_result *ptr); -struct json_result *new_json_result(const tal_t *ctx); +/** + * json_stream_success - start streaming a successful json result. + * @cmd: the command we're running. + * + * The returned value should go to command_success() when done. + * json_add_* will be placed into the 'result' field of the JSON reply. + */ +struct json_result *json_stream_success(struct command *cmd); + +/** + * json_stream_fail - start streaming a failed json result. + * @cmd: the command we're running. + * @code: the error code from lightningd/jsonrpc_errors.h + * @errmsg: the error string. + * + * The returned value should go to command_failed() when done; + * json_add_* will be placed into the 'data' field of the 'error' JSON reply. + */ +struct json_result *json_stream_fail(struct command *cmd, + int code, + const char *errmsg); /* '"fieldname" : "value"' or '"value"' if fieldname is NULL. Turns * any non-printable chars into JSON escapes, but leaves existing escapes alone. diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index ed1dc4d77..a9201bd91 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -73,13 +73,14 @@ AUTODATA(json_command, &help_command); static void json_stop(struct command *cmd, const char *buffer UNUSED, const jsmntok_t *params UNUSED) { - struct json_result *response = new_json_result(cmd); + struct json_result *response; if (!param(cmd, buffer, params, NULL)) return; /* This can't have closed yet! */ cmd->jcon->stop = true; + response = json_stream_success(cmd); json_add_string(response, NULL, "Shutting down"); command_success(cmd, response); } @@ -95,7 +96,7 @@ AUTODATA(json_command, &stop_command); static void json_rhash(struct command *cmd, const char *buffer, const jsmntok_t *params) { - struct json_result *response = new_json_result(cmd); + struct json_result *response; struct sha256 *secret; if (!param(cmd, buffer, params, @@ -105,6 +106,7 @@ static void json_rhash(struct command *cmd, /* Hash in place. */ sha256(secret, secret, sizeof(*secret)); + response = json_stream_success(cmd); json_object_start(response, NULL); json_add_hex(response, "rhash", secret, sizeof(*secret)); json_object_end(response); @@ -138,11 +140,12 @@ AUTODATA(json_command, &dev_crash_command); static void json_getinfo(struct command *cmd, const char *buffer UNUSED, const jsmntok_t *params UNUSED) { - struct json_result *response = new_json_result(cmd); + struct json_result *response; if (!param(cmd, buffer, params, NULL)) return; + response = json_stream_success(cmd); json_object_start(response, NULL); json_add_pubkey(response, "id", &cmd->ld->id); json_add_string(response, "alias", (const char *)cmd->ld->alias); @@ -222,7 +225,7 @@ static void json_help(struct command *cmd, const char *buffer, const jsmntok_t *params) { unsigned int i; - struct json_result *response = new_json_result(cmd); + struct json_result *response; struct json_command **cmdlist = get_cmdlist(); const jsmntok_t *cmdtok; @@ -234,6 +237,7 @@ static void json_help(struct command *cmd, if (cmdtok) { for (i = 0; i < num_cmdlist; i++) { if (json_tok_streq(buffer, cmdtok, cmdlist[i]->name)) { + response = json_stream_success(cmd); json_add_help_command(cmd, response, cmdlist[i]); goto done; } @@ -245,6 +249,7 @@ static void json_help(struct command *cmd, return; } + response = json_stream_success(cmd); json_object_start(response, NULL); json_array_start(response, "help"); for (i = 0; i < num_cmdlist; i++) { @@ -341,11 +346,11 @@ static void connection_complete_error(struct json_connection *jcon, id))); } -struct json_result *null_response(const tal_t *ctx) +struct json_result *null_response(struct command *cmd) { struct json_result *response; - response = new_json_result(ctx); + response = json_stream_success(cmd); json_object_start(response, NULL); json_object_end(response); return response; @@ -365,12 +370,11 @@ void command_success(struct command *cmd, struct json_result *result) connection_complete_ok(jcon, cmd, cmd->id, result); } -static void command_fail_v(struct command *cmd, - int code, - const struct json_result *data, - const char *fmt, va_list ap) +static void command_fail_generic(struct command *cmd, + int code, + const struct json_result *data, + const char *error) { - char *error; struct json_connection *jcon = cmd->jcon; if (!jcon) { @@ -381,8 +385,6 @@ static void command_fail_v(struct command *cmd, return; } - error = tal_vfmt(cmd, fmt, ap); - /* cmd->json_cmd can be NULL, if we're failing for command not found! */ log_debug(jcon->log, "Failing %s: %s", cmd->json_cmd ? cmd->json_cmd->name : "invalid cmd", @@ -392,21 +394,18 @@ static void command_fail_v(struct command *cmd, connection_complete_error(jcon, cmd, cmd->id, error, code, data); } -void command_fail(struct command *cmd, int code, const char *fmt, ...) +void command_failed(struct command *cmd, struct json_result *result) { - va_list ap; - va_start(ap, fmt); - command_fail_v(cmd, code, NULL, fmt, ap); - va_end(ap); + assert(cmd->failcode != 0); + command_fail_generic(cmd, cmd->failcode, result, cmd->errmsg); } -void command_fail_detailed(struct command *cmd, - int code, const struct json_result *data, - const char *fmt, ...) +void PRINTF_FMT(3, 4) command_fail(struct command *cmd, int code, + const char *fmt, ...) { va_list ap; va_start(ap, fmt); - command_fail_v(cmd, code, data, fmt, ap); + command_fail_generic(cmd, code, NULL, tal_vfmt(cmd, fmt, ap)); va_end(ap); } @@ -457,6 +456,7 @@ static bool parse_request(struct json_connection *jcon, const jsmntok_t tok[]) c->jcon = jcon; c->ld = jcon->ld; c->pending = false; + c->have_json_stream = false; c->id = tal_strndup(c, json_tok_contents(jcon->buffer, id), json_tok_len(id)); diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index 1def17f3e..f829d43c3 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -38,6 +38,12 @@ struct command { /* This is created if mode is CMD_USAGE */ const char *usage; bool *ok; + /* Have we started a json stream already? For debugging. */ + bool have_json_stream; + + /* FIXME: Temporary. */ + int failcode; + const char *errmsg; }; struct json_connection { @@ -79,14 +85,11 @@ struct json_command { const char *verbose; }; -struct json_result *null_response(const tal_t *ctx); +struct json_result *null_response(struct command *cmd); void command_success(struct command *cmd, struct json_result *response); +void command_failed(struct command *cmd, struct json_result *result); void PRINTF_FMT(3, 4) command_fail(struct command *cmd, int code, const char *fmt, ...); -void PRINTF_FMT(4, 5) command_fail_detailed(struct command *cmd, - int code, - const struct json_result *data, - const char *fmt, ...); /* Mainly for documentation, that we plan to close this later. */ void command_still_pending(struct command *cmd); diff --git a/lightningd/log.c b/lightningd/log.c index ee89e9a4a..98dbb6f17 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -718,7 +718,7 @@ bool json_tok_loglevel(struct command *cmd, const char *name, static void json_getlog(struct command *cmd, const char *buffer, const jsmntok_t * params) { - struct json_result *response = new_json_result(cmd); + struct json_result *response; enum log_level *minlevel; struct log_book *lr = cmd->ld->log_book; @@ -728,6 +728,7 @@ static void json_getlog(struct command *cmd, NULL)) return; + response = json_stream_success(cmd); json_object_start(response, NULL); json_add_time(response, "created_at", log_init_time(lr)->ts); json_add_num(response, "bytes_used", (unsigned int) log_used(lr)); diff --git a/lightningd/memdump.c b/lightningd/memdump.c index e8b9f6bb1..d154cced6 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -56,11 +56,12 @@ static void json_memdump(struct command *cmd, const char *buffer UNNEEDED, const jsmntok_t *params UNNEEDED) { - struct json_result *response = new_json_result(cmd); + struct json_result *response; if (!param(cmd, buffer, params, NULL)) return; + response = json_stream_success(cmd); add_memdump(response, NULL, NULL, cmd); command_success(cmd, response); @@ -153,7 +154,7 @@ static void json_memleak(struct command *cmd, const char *buffer UNNEEDED, const jsmntok_t *params UNNEEDED) { - struct json_result *response = new_json_result(cmd); + struct json_result *response; if (!param(cmd, buffer, params, NULL)) return; @@ -164,6 +165,7 @@ static void json_memleak(struct command *cmd, return; } + response = json_stream_success(cmd); json_object_start(response, NULL); scan_mem(cmd, response, cmd->ld); json_object_end(response); diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index c008eb9ef..e4ea93732 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -377,7 +377,7 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp, wallet_confirm_utxos(ld->wallet, fc->wtx.utxos); - response = new_json_result(fc->cmd); + response = json_stream_success(fc->cmd); json_object_start(response, NULL); linear = linearize_tx(response, fundingtx); json_add_hex_talarr(response, "tx", linear); diff --git a/lightningd/options.c b/lightningd/options.c index b017e8246..2fa312c24 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -978,18 +978,19 @@ static void json_listconfigs(struct command *cmd, const char *buffer, const jsmntok_t *params) { size_t i; - struct json_result *response = new_json_result(cmd); + struct json_result *response = NULL; const jsmntok_t *configtok; - bool found = false; if (!param(cmd, buffer, params, p_opt("config", json_tok_tok, &configtok), NULL)) return; - json_object_start(response, NULL); - if (!configtok) + if (!configtok) { + response = json_stream_success(cmd); + json_object_start(response, NULL); json_add_string(response, "# version", version()); + } for (i = 0; i < opt_count; i++) { unsigned int len; @@ -1012,20 +1013,23 @@ static void json_listconfigs(struct command *cmd, name + 1, len - 1)) continue; - found = true; + if (!response) { + response = json_stream_success(cmd); + json_object_start(response, NULL); + } add_config(cmd->ld, response, &opt_table[i], name+1, len-1); } } - json_object_end(response); - if (configtok && !found) { + if (configtok && !response) { command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Unknown config option '%.*s'", configtok->end - configtok->start, buffer + configtok->start); return; } + json_object_end(response); command_success(cmd, response); } diff --git a/lightningd/pay.c b/lightningd/pay.c index 4d511160a..a79a6ad18 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -851,7 +851,7 @@ json_sendpay_success(struct command *cmd, assert(r->payment->status == PAYMENT_COMPLETE); - response = new_json_result(cmd); + response = json_stream_success(cmd); json_object_start(response, NULL); json_add_payment_fields(response, r->payment); json_object_end(response); @@ -863,13 +863,13 @@ static void json_waitsendpay_on_resolve(const struct sendpay_result *r, { struct command *cmd = (struct command*) vcmd; - struct json_result *data = NULL; const char *msg = NULL; struct routing_failure *fail; if (r->succeeded) json_sendpay_success(cmd, r); else { + struct json_result *data; switch (r->errorcode) { /* We will never handle this case */ case PAY_IN_PROGRESS: @@ -878,28 +878,30 @@ static void json_waitsendpay_on_resolve(const struct sendpay_result *r, case PAY_RHASH_ALREADY_USED: case PAY_UNSPECIFIED_ERROR: case PAY_NO_SUCH_PAYMENT: - data = NULL; - msg = r->details; - break; + command_fail(cmd, r->errorcode, "%s", r->details); + return; case PAY_UNPARSEABLE_ONION: - data = new_json_result(cmd); - json_object_start(data, NULL); - json_add_hex_talarr(data, "onionreply", r->onionreply); - json_object_end(data); - - assert(r->details != NULL); - msg = tal_fmt(cmd, + msg = tal_fmt(tmpctx, "failed: WIRE_PERMANENT_NODE_FAILURE " "(%s)", r->details); - break; + data = json_stream_fail(cmd, r->errorcode, msg); + json_object_start(data, NULL); + json_add_hex_talarr(data, "onionreply", r->onionreply); + json_object_end(data); + command_failed(cmd, data); + return; case PAY_DESTINATION_PERM_FAIL: case PAY_TRY_OTHER_ROUTE: fail = r->routing_failure; - data = new_json_result(cmd); + msg = tal_fmt(cmd, + "failed: %s (%s)", + onion_type_name(fail->failcode), + r->details); + data = json_stream_fail(cmd, r->errorcode, msg); json_object_start(data, NULL); json_add_num(data, "erring_index", @@ -913,18 +915,10 @@ static void json_waitsendpay_on_resolve(const struct sendpay_result *r, json_add_hex_talarr(data, "channel_update", fail->channel_update); json_object_end(data); - - assert(r->details != NULL); - msg = tal_fmt(cmd, - "failed: %s (%s)", - onion_type_name(fail->failcode), - r->details); - - break; + command_failed(cmd, data); + return; } - - assert(msg); - command_fail_detailed(cmd, r->errorcode, data, "%s", msg); + abort(); } } @@ -935,7 +929,7 @@ static void json_sendpay_on_resolve(const struct sendpay_result* r, if (!r->succeeded && r->errorcode == PAY_IN_PROGRESS) { /* This is normal for sendpay. Succeed. */ - struct json_result *response = new_json_result(cmd); + struct json_result *response = json_stream_success(cmd); json_object_start(response, NULL); json_add_string(response, "message", "Monitor status with listpayments or waitsendpay"); @@ -1064,7 +1058,7 @@ static void json_listpayments(struct command *cmd, const char *buffer, const jsmntok_t *params) { const struct wallet_payment **payments; - struct json_result *response = new_json_result(cmd); + struct json_result *response; struct sha256 *rhash; const char *b11str; @@ -1096,6 +1090,7 @@ static void json_listpayments(struct command *cmd, const char *buffer, payments = wallet_payment_list(cmd, cmd->ld->wallet, rhash); + response = json_stream_success(cmd); json_object_start(response, NULL); json_array_start(response, "payments"); diff --git a/lightningd/payalgo.c b/lightningd/payalgo.c index f5d412e7a..157f48417 100644 --- a/lightningd/payalgo.c +++ b/lightningd/payalgo.c @@ -196,7 +196,7 @@ json_pay_success(struct pay *pay, struct command *cmd = pay->cmd; struct json_result *response; - response = new_json_result(cmd); + response = json_stream_success(cmd); json_object_start(response, NULL); json_add_payment_fields(response, r->payment); json_add_num(response, "getroute_tries", pay->getroute_tries); @@ -212,42 +212,44 @@ static void json_pay_failure(struct pay *pay, const struct sendpay_result *r) { struct json_result *data; - const char *msg = NULL; struct routing_failure *fail; assert(!r->succeeded); - data = new_json_result(pay); - switch (r->errorcode) { case PAY_IN_PROGRESS: + data = json_stream_fail(pay->cmd, r->errorcode, r->details); json_object_start(data, NULL); json_add_num(data, "getroute_tries", pay->getroute_tries); json_add_num(data, "sendpay_tries", pay->sendpay_tries); json_add_payment_fields(data, r->payment); json_add_failures(data, "failures", &pay->pay_failures); json_object_end(data); - msg = r->details; - break; + return; case PAY_RHASH_ALREADY_USED: case PAY_STOPPED_RETRYING: + data = json_stream_fail(pay->cmd, r->errorcode, r->details); json_object_start(data, NULL); json_add_num(data, "getroute_tries", pay->getroute_tries); json_add_num(data, "sendpay_tries", pay->sendpay_tries); json_add_failures(data, "failures", &pay->pay_failures); json_object_end(data); - msg = r->details; - break; + return; case PAY_UNPARSEABLE_ONION: /* Impossible case */ abort(); - break; case PAY_DESTINATION_PERM_FAIL: fail = r->routing_failure; + assert(r->details != NULL); + data = json_stream_fail(pay->cmd, + r->errorcode, + tal_fmt(tmpctx, "failed: %s (%s)", + onion_type_name(fail->failcode), + r->details)); json_object_start(data, NULL); json_add_num(data, "erring_index", fail->erring_index); @@ -261,23 +263,14 @@ static void json_pay_failure(struct pay *pay, fail->channel_update); json_add_failures(data, "failures", &pay->pay_failures); json_object_end(data); - - assert(r->details != NULL); - msg = tal_fmt(pay, - "failed: %s (%s)", - onion_type_name(fail->failcode), - r->details); - - break; + return; case PAY_TRY_OTHER_ROUTE: /* Impossible case */ abort(); - break; + return; } - - assert(msg); - command_fail_detailed(pay->cmd, r->errorcode, data, "%s", msg); + abort(); } /* Determine if we should delay before retrying. Return a reason @@ -422,14 +415,14 @@ static void json_pay_getroute_reply(struct subd *gossip UNUSED, fromwire_gossip_getroute_reply(reply, reply, &route); if (tal_count(route) == 0) { - data = new_json_result(pay); + data = json_stream_fail(pay->cmd, PAY_ROUTE_NOT_FOUND, + "Could not find a route"); json_object_start(data, NULL); json_add_num(data, "getroute_tries", pay->getroute_tries); json_add_num(data, "sendpay_tries", pay->sendpay_tries); json_add_failures(data, "failures", &pay->pay_failures); json_object_end(data); - command_fail_detailed(pay->cmd, PAY_ROUTE_NOT_FOUND, data, - "Could not find a route"); + command_failed(pay->cmd, data); return; } @@ -444,21 +437,6 @@ static void json_pay_getroute_reply(struct subd *gossip UNUSED, delay_too_high = (route[0].delay > pay->maxdelay); /* compare fuzz to range */ if ((fee_too_high || delay_too_high) && pay->fuzz < 0.01) { - data = new_json_result(pay); - json_object_start(data, NULL); - json_add_u64(data, "msatoshi", pay->msatoshi); - json_add_u64(data, "fee", fee); - json_add_double(data, "feepercent", feepercent); - json_add_double(data, "maxfeepercent", pay->maxfeepercent); - json_add_u64(data, "delay", (u64) route[0].delay); - json_add_num(data, "maxdelay", pay->maxdelay); - json_add_num(data, "getroute_tries", pay->getroute_tries); - json_add_num(data, "sendpay_tries", pay->sendpay_tries); - json_add_route(data, "route", - route, tal_count(route)); - json_add_failures(data, "failures", &pay->pay_failures); - json_object_end(data); - err = ""; if (fee_too_high) err = tal_fmt(pay, @@ -477,9 +455,22 @@ static void json_pay_getroute_reply(struct subd *gossip UNUSED, "max delay requested is %u.", err, route[0].delay, pay->maxdelay); + data = json_stream_fail(pay->cmd, PAY_ROUTE_TOO_EXPENSIVE, err); + json_object_start(data, NULL); + json_add_u64(data, "msatoshi", pay->msatoshi); + json_add_u64(data, "fee", fee); + json_add_double(data, "feepercent", feepercent); + json_add_double(data, "maxfeepercent", pay->maxfeepercent); + json_add_u64(data, "delay", (u64) route[0].delay); + json_add_num(data, "maxdelay", pay->maxdelay); + json_add_num(data, "getroute_tries", pay->getroute_tries); + json_add_num(data, "sendpay_tries", pay->sendpay_tries); + json_add_route(data, "route", + route, tal_count(route)); + json_add_failures(data, "failures", &pay->pay_failures); + json_object_end(data); - command_fail_detailed(pay->cmd, PAY_ROUTE_TOO_EXPENSIVE, - data, "%s", err); + command_failed(pay->cmd, data); return; } if (fee_too_high || delay_too_high) { @@ -519,7 +510,9 @@ static bool json_pay_try(struct pay *pay) /* If too late anyway, fail now. */ if (time_after(now, pay->expiry)) { - struct json_result *data = new_json_result(cmd); + struct json_result *data + = json_stream_fail(cmd, PAY_INVOICE_EXPIRED, + "Invoice expired"); json_object_start(data, NULL); json_add_num(data, "now", now.ts.tv_sec); json_add_num(data, "expiry", pay->expiry.ts.tv_sec); @@ -527,8 +520,7 @@ static bool json_pay_try(struct pay *pay) json_add_num(data, "sendpay_tries", pay->sendpay_tries); json_add_failures(data, "failures", &pay->pay_failures); json_object_end(data); - command_fail_detailed(cmd, PAY_INVOICE_EXPIRED, data, - "Invoice expired"); + command_failed(cmd, data); return false; } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 7c90083bf..ae0e3d275 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -232,7 +232,7 @@ static void remove_sig(struct bitcoin_tx *signed_tx) static void resolve_one_close_command(struct close_command *cc, bool cooperative) { - struct json_result *result = new_json_result(cc); + struct json_result *result = json_stream_success(cc->cmd); u8 *tx = linearize_tx(result, cc->channel->last_tx); struct bitcoin_txid txid; @@ -818,7 +818,7 @@ static void json_listpeers(struct command *cmd, enum log_level *ll; struct pubkey *specific_id; struct peer *peer; - struct json_result *response = new_json_result(cmd); + struct json_result *response; if (!param(cmd, buffer, params, p_opt("id", json_tok_pubkey, &specific_id), @@ -826,6 +826,7 @@ static void json_listpeers(struct command *cmd, NULL)) return; + response = json_stream_success(cmd); json_object_start(response, NULL); json_array_start(response, "peers"); if (specific_id) { @@ -1083,7 +1084,7 @@ static void json_sign_last_tx(struct command *cmd, { struct pubkey *peerid; struct peer *peer; - struct json_result *response = new_json_result(cmd); + struct json_result *response; u8 *linear; struct channel *channel; @@ -1105,6 +1106,7 @@ static void json_sign_last_tx(struct command *cmd, return; } + response = json_stream_success(cmd); log_debug(channel->log, "dev-sign-last-tx: signing tx with %zu outputs", tal_count(channel->last_tx->output)); sign_last_tx(channel); @@ -1244,7 +1246,7 @@ static void process_dev_forget_channel(struct bitcoind *bitcoind UNUSED, "channel"); return; } - response = new_json_result(forget->cmd); + response = json_stream_success(forget->cmd); json_object_start(response, NULL); json_add_bool(response, "forced", forget->force); json_add_bool(response, "funding_unspent", txout != NULL); diff --git a/lightningd/ping.c b/lightningd/ping.c index 23cb4f251..fb3eea753 100644 --- a/lightningd/ping.c +++ b/lightningd/ping.c @@ -69,7 +69,7 @@ void ping_reply(struct subd *subd, const u8 *msg) else if (!sent) command_fail(pc->cmd, LIGHTNINGD, "Unknown peer"); else { - struct json_result *response = new_json_result(pc->cmd); + struct json_result *response = json_stream_success(pc->cmd); json_object_start(response, NULL); json_add_num(response, "totlen", totlen); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index a15108e01..a0277c927 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -48,12 +48,9 @@ bool channel_tell_funding_locked(struct lightningd *ld UNNEEDED, void command_fail(struct command *cmd UNNEEDED, int code UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "command_fail called!\n"); abort(); } -/* Generated stub for command_fail_detailed */ -void command_fail_detailed(struct command *cmd UNNEEDED, - int code UNNEEDED, - const struct json_result *data UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "command_fail_detailed called!\n"); abort(); } +/* Generated stub for command_failed */ +void command_failed(struct command *cmd UNNEEDED, struct json_result *result UNNEEDED) +{ fprintf(stderr, "command_failed called!\n"); abort(); } /* Generated stub for command_still_pending */ void command_still_pending(struct command *cmd UNNEEDED) { fprintf(stderr, "command_still_pending called!\n"); abort(); } @@ -162,6 +159,14 @@ void json_object_end(struct json_result *ptr UNNEEDED) /* Generated stub for json_object_start */ void json_object_start(struct json_result *ptr UNNEEDED, const char *fieldname UNNEEDED) { fprintf(stderr, "json_object_start called!\n"); abort(); } +/* Generated stub for json_stream_fail */ +struct json_result *json_stream_fail(struct command *cmd UNNEEDED, + int code UNNEEDED, + const char *errmsg UNNEEDED) +{ fprintf(stderr, "json_stream_fail called!\n"); abort(); } +/* Generated stub for json_stream_success */ +struct json_result *json_stream_success(struct command *cmd UNNEEDED) +{ fprintf(stderr, "json_stream_success called!\n"); abort(); } /* Generated stub for json_tok_address_scriptpubkey */ enum address_parse_result json_tok_address_scriptpubkey(const tal_t *ctx UNNEEDED, const struct chainparams *chainparams UNNEEDED, @@ -258,9 +263,6 @@ void log_io(struct log *log UNNEEDED, enum log_level dir UNNEEDED, const char *c /* Generated stub for new_bolt11 */ struct bolt11 *new_bolt11(const tal_t *ctx UNNEEDED, u64 *msatoshi UNNEEDED) { fprintf(stderr, "new_bolt11 called!\n"); abort(); } -/* Generated stub for new_json_result */ -struct json_result *new_json_result(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "new_json_result called!\n"); abort(); } /* Generated stub for new_log */ struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "new_log called!\n"); abort(); } @@ -275,7 +277,7 @@ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "new_reltimer_ called!\n"); abort(); } /* Generated stub for null_response */ -struct json_result *null_response(const tal_t *ctx UNNEEDED) +struct json_result *null_response(struct command *cmd UNNEEDED) { fprintf(stderr, "null_response called!\n"); abort(); } /* Generated stub for onchaind_funding_spent */ enum watch_result onchaind_funding_spent(struct channel *channel UNNEEDED, diff --git a/lightningd/test/run-jsonrpc.c b/lightningd/test/run-jsonrpc.c index 500f73c20..f76e8fe4c 100644 --- a/lightningd/test/run-jsonrpc.c +++ b/lightningd/test/run-jsonrpc.c @@ -70,7 +70,8 @@ bool deprecated_apis; static int test_json_filter(void) { - struct json_result *result = new_json_result(NULL); + struct command *cmd = talz(NULL, struct command); + struct json_result *result = json_stream_success(cmd); jsmntok_t *toks; const jsmntok_t *x; bool valid; @@ -105,7 +106,7 @@ static int test_json_filter(void) assert((unsigned)str[i] >= ' '); assert((unsigned)str[i] != 127); } - tal_free(result); + tal_free(cmd); return 0; } @@ -115,7 +116,8 @@ static void test_json_escape(void) for (i = 1; i < 256; i++) { char badstr[2]; - struct json_result *result = new_json_result(NULL); + struct command *cmd = talz(NULL, struct command); + struct json_result *result = json_stream_success(cmd); struct json_escaped *esc; badstr[0] = i; @@ -138,7 +140,7 @@ static void test_json_escape(void) expect[11] = i; assert(streq(str, expect)); } - tal_free(result); + tal_free(cmd); } } diff --git a/lightningd/test/run-param.c b/lightningd/test/run-param.c index 5e98e3ad0..f9675597d 100644 --- a/lightningd/test/run-param.c +++ b/lightningd/test/run-param.c @@ -30,18 +30,6 @@ void command_fail(struct command *cmd, int code, const char *fmt, ...) va_end(ap); } -void command_fail_detailed(struct command *cmd, int code, - const struct json_result *data, const char *fmt, ...) -{ - failed = true; - va_list ap; - va_start(ap, fmt); - fail_msg = tal_vfmt(cmd, fmt, ap); - fail_msg = - tal_fmt(cmd, "%s data: %s", fail_msg, json_result_string(data)); - va_end(ap); -} - /* AUTOGENERATED MOCKS START */ /* Generated stub for feerate_from_style */ u32 feerate_from_style(u32 feerate UNNEEDED, enum feerate_style style UNNEEDED) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 9dc03c090..72fceda82 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -234,6 +234,9 @@ void json_object_end(struct json_result *ptr UNNEEDED) /* Generated stub for json_object_start */ void json_object_start(struct json_result *ptr UNNEEDED, const char *fieldname UNNEEDED) { fprintf(stderr, "json_object_start called!\n"); abort(); } +/* Generated stub for json_stream_success */ +struct json_result *json_stream_success(struct command *cmd UNNEEDED) +{ fprintf(stderr, "json_stream_success called!\n"); abort(); } /* Generated stub for json_tok_bool */ bool json_tok_bool(struct command *cmd UNNEEDED, const char *name UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, @@ -287,11 +290,8 @@ void log_add(struct log *log UNNEEDED, const char *fmt UNNEEDED, ...) void log_io(struct log *log UNNEEDED, enum log_level dir UNNEEDED, const char *comment UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "log_io called!\n"); abort(); } -/* Generated stub for new_json_result */ -struct json_result *new_json_result(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "new_json_result called!\n"); abort(); } /* Generated stub for null_response */ -struct json_result *null_response(const tal_t *ctx UNNEEDED) +struct json_result *null_response(struct command *cmd UNNEEDED) { fprintf(stderr, "null_response called!\n"); abort(); } /* Generated stub for onchaind_funding_spent */ enum watch_result onchaind_funding_spent(struct channel *channel UNNEEDED, diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 69372eeb6..586ac402a 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -65,7 +65,7 @@ static void wallet_withdrawal_broadcast(struct bitcoind *bitcoind UNUSED, * not if we're actually making a payment to ourselves! */ assert(change_satoshi >= withdraw->wtx.change); - struct json_result *response = new_json_result(cmd); + struct json_result *response = json_stream_success(cmd); json_object_start(response, NULL); json_add_string(response, "tx", withdraw->hextx); json_add_string(response, "txid", output); @@ -242,7 +242,7 @@ static bool json_tok_newaddr(struct command *cmd, const char *name, static void json_newaddr(struct command *cmd, const char *buffer UNUSED, const jsmntok_t *params UNUSED) { - struct json_result *response = new_json_result(cmd); + struct json_result *response; struct ext_key ext; struct pubkey pubkey; bool *is_p2wpkh; @@ -283,6 +283,7 @@ static void json_newaddr(struct command *cmd, const char *buffer UNUSED, return; } + response = json_stream_success(cmd); json_object_start(response, NULL); json_add_string(response, "address", out); json_object_end(response); @@ -300,7 +301,7 @@ AUTODATA(json_command, &newaddr_command); static void json_listaddrs(struct command *cmd, const char *buffer, const jsmntok_t *params) { - struct json_result *response = new_json_result(cmd); + struct json_result *response; struct ext_key ext; struct pubkey pubkey; u64 *bip32_max_index; @@ -312,6 +313,7 @@ static void json_listaddrs(struct command *cmd, NULL)) return; + response = json_stream_success(cmd); json_object_start(response, NULL); json_array_start(response, "addresses"); @@ -323,15 +325,12 @@ static void json_listaddrs(struct command *cmd, if (bip32_key_from_parent(cmd->ld->wallet->bip32_base, keyidx, BIP32_FLAG_KEY_PUBLIC, &ext) != WALLY_OK) { - command_fail(cmd, LIGHTNINGD, - "Keys generation failure"); - return; + abort(); } if (!secp256k1_ec_pubkey_parse(secp256k1_ctx, &pubkey.pubkey, ext.pub_key, sizeof(ext.pub_key))) { - command_fail(cmd, LIGHTNINGD, "Key parsing failure"); - return; + abort(); } // p2sh @@ -348,9 +347,7 @@ static void json_listaddrs(struct command *cmd, false, &redeemscript_p2wpkh); if (!out_p2wpkh) { - command_fail(cmd, LIGHTNINGD, - "p2wpkh address encoding failure."); - return; + abort(); } // outputs @@ -382,16 +379,17 @@ AUTODATA(json_command, &listaddrs_command); static void json_listfunds(struct command *cmd, const char *buffer UNUSED, const jsmntok_t *params UNUSED) { - struct json_result *response = new_json_result(cmd); + struct json_result *response; struct peer *p; - struct utxo **utxos = - wallet_get_utxos(cmd, cmd->ld->wallet, output_state_available); + struct utxo **utxos; char* out; struct pubkey funding_pubkey; if (!param(cmd, buffer, params, NULL)) return; + utxos = wallet_get_utxos(cmd, cmd->ld->wallet, output_state_available); + response = json_stream_success(cmd); json_object_start(response, NULL); json_array_start(response, "outputs"); for (size_t i = 0; i < tal_count(utxos); i++) { @@ -513,7 +511,7 @@ static void json_dev_rescan_outputs(struct command *cmd, if (!param(cmd, buffer, params, NULL)) return; - rescan->response = new_json_result(cmd); + rescan->response = json_stream_success(cmd); rescan->cmd = cmd; /* Open the result structure so we can incrementally add results */