common/json: make json_scan return an error string.

This makes for more useful errors.  It prints where it was up to in
the guide, but doesn't print the entire JSON it's scanning.

Suggested-by: Christian Decker
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2021-01-07 16:04:43 +10:30
committed by Christian Decker
parent 53582a0f81
commit 3b7d0e7a62
9 changed files with 381 additions and 265 deletions

View File

@@ -326,6 +326,7 @@ static struct command_result *process_getutxout(struct bitcoin_cli *bcli)
const jsmntok_t *tokens;
struct json_stream *response;
struct bitcoin_tx_output output;
const char *err;
/* As of at least v0.15.1.0, bitcoind returns "success" but an empty
string on a spent txout. */
@@ -343,14 +344,14 @@ static struct command_result *process_getutxout(struct bitcoin_cli *bcli)
return command_err_bcli_badjson(bcli, "cannot parse");
}
if (!json_scan(bcli->output, tokens,
err = json_scan(tmpctx, bcli->output, tokens,
"{value:%,scriptPubKey:{hex:%}}",
JSON_SCAN(json_to_bitcoin_amount,
&output.amount.satoshis), /* Raw: bitcoind */
JSON_SCAN_TAL(bcli, json_tok_bin_from_hex,
&output.script))) {
return command_err_bcli_badjson(bcli, "cannot scan");
}
&output.script));
if (err)
return command_err_bcli_badjson(bcli, err);
response = jsonrpc_stream_success(bcli->cmd);
json_add_amount_sat_only(response, "amount", output.amount);
@@ -365,7 +366,7 @@ static struct command_result *process_getblockchaininfo(struct bitcoin_cli *bcli
struct json_stream *response;
bool ibd;
u32 headers, blocks;
const char *chain;
const char *chain, *err;
tokens = json_parse_simple(bcli->output,
bcli->output, bcli->output_bytes);
@@ -373,14 +374,14 @@ static struct command_result *process_getblockchaininfo(struct bitcoin_cli *bcli
return command_err_bcli_badjson(bcli, "cannot parse");
}
if (!json_scan(bcli->output, tokens,
"{chain:%,headers:%,blocks:%,initialblockdownload:%}",
JSON_SCAN_TAL(tmpctx, json_strdup, &chain),
JSON_SCAN(json_to_number, &headers),
JSON_SCAN(json_to_number, &blocks),
JSON_SCAN(json_to_bool, &ibd))) {
return command_err_bcli_badjson(bcli, "cannot scan");
}
err = json_scan(tmpctx, bcli->output, tokens,
"{chain:%,headers:%,blocks:%,initialblockdownload:%}",
JSON_SCAN_TAL(tmpctx, json_strdup, &chain),
JSON_SCAN(json_to_number, &headers),
JSON_SCAN(json_to_number, &blocks),
JSON_SCAN(json_to_bool, &ibd));
if (err)
return command_err_bcli_badjson(bcli, err);
response = jsonrpc_stream_success(bcli->cmd);
json_add_string(response, "chain", chain);
@@ -425,8 +426,8 @@ estimatefees_parse_feerate(struct bitcoin_cli *bcli, u64 *feerate)
return command_err_bcli_badjson(bcli, "cannot parse");
}
if (!json_scan(bcli->output, tokens, "{feerate:%}",
JSON_SCAN(json_to_bitcoin_amount, feerate))) {
if (json_scan(tmpctx, bcli->output, tokens, "{feerate:%}",
JSON_SCAN(json_to_bitcoin_amount, feerate)) != NULL) {
/* Paranoia: if it had a feerate, but was malformed: */
if (json_get_member(bcli->output, tokens, "feerate"))
return command_err_bcli_badjson(bcli, "cannot scan");
@@ -782,6 +783,7 @@ static void parse_getnetworkinfo_result(struct plugin *p, const char *buf)
const jsmntok_t *result;
bool tx_relay;
u32 min_version = 160000;
const char *err;
result = json_parse_simple(NULL, buf, strlen(buf));
if (!result)
@@ -790,12 +792,14 @@ static void parse_getnetworkinfo_result(struct plugin *p, const char *buf)
gather_args(bitcoind, "getnetworkinfo", NULL), buf);
/* Check that we have a fully-featured `estimatesmartfee`. */
if (!json_scan(buf, result, "{version:%,localrelay:%}",
JSON_SCAN(json_to_u32, &bitcoind->version),
JSON_SCAN(json_to_bool, &tx_relay)))
plugin_err(p, "No 'version' or localrelay in '%s' ? Got '%s'. Can not"
" continue without proceeding to sanity checks.",
gather_args(bitcoind, "getnetworkinfo", NULL), buf);
err = json_scan(tmpctx, buf, result, "{version:%,localrelay:%}",
JSON_SCAN(json_to_u32, &bitcoind->version),
JSON_SCAN(json_to_bool, &tx_relay));
if (err)
plugin_err(p, "%s. Got '%s'. Can not"
" continue without proceeding to sanity checks.",
err,
gather_args(bitcoind, "getnetworkinfo", NULL), buf);
if (bitcoind->version < min_version)
plugin_err(p, "Unsupported bitcoind version %"PRIu32", at least"

View File

@@ -251,10 +251,13 @@ static struct command_result *htlc_accepted_call(struct command *cmd,
struct keysend_in *ki;
struct out_req *req;
struct timeabs now = time_now();
const char *err;
if (!json_scan(buf, params, "{onion:{payload:%},htlc:{payment_hash:%}}",
JSON_SCAN_TAL(cmd, json_tok_bin_from_hex, &rawpayload),
JSON_SCAN(json_to_sha256, &payment_hash)))
err = json_scan(tmpctx, buf, params,
"{onion:{payload:%},htlc:{payment_hash:%}}",
JSON_SCAN_TAL(cmd, json_tok_bin_from_hex, &rawpayload),
JSON_SCAN(json_to_sha256, &payment_hash));
if (err)
return htlc_accepted_continue(cmd, NULL);
max = tal_bytelen(rawpayload);

View File

@@ -498,6 +498,7 @@ void rpc_scan(struct plugin *plugin,
...)
{
bool error;
const char *err;
const jsmntok_t *contents;
int reqlen;
const char *p;
@@ -515,12 +516,13 @@ void rpc_scan(struct plugin *plugin,
p = membuf_consume(&plugin->rpc_conn->mb, reqlen);
va_start(ap, guide);
error = !json_scanv(p, contents, guide, ap);
err = json_scanv(tmpctx, p, contents, guide, ap);
va_end(ap);
if (error)
plugin_err(plugin, "Could not parse %s in reply to %s: '%.*s'",
guide, method, reqlen, membuf_elems(&plugin->rpc_conn->mb));
if (err)
plugin_err(plugin, "Could not parse %s in reply to %s: %s: '%.*s'",
guide, method, err,
reqlen, membuf_elems(&plugin->rpc_conn->mb));
}
static void handle_rpc_reply(struct plugin *plugin, const jsmntok_t *toks)
@@ -810,19 +812,21 @@ static struct command_result *handle_init(struct command *cmd,
char *dir, *network;
struct plugin *p = cmd->plugin;
bool with_rpc = p->rpc_conn != NULL;
const char *err;
configtok = json_get_member(buf, params, "configuration");
if (!json_scan(buf, configtok,
"{lightning-dir:%"
",network:%"
",feature_set:%"
",rpc-file:%}",
JSON_SCAN_TAL(tmpctx, json_strdup, &dir),
JSON_SCAN_TAL(tmpctx, json_strdup, &network),
JSON_SCAN_TAL(p, json_to_feature_set, &p->our_features),
JSON_SCAN_TAL(p, json_strdup, &p->rpc_location)))
plugin_err(p, "cannot scan init params: %.*s",
json_tok_full_len(params),
err = json_scan(tmpctx, buf, configtok,
"{lightning-dir:%"
",network:%"
",feature_set:%"
",rpc-file:%}",
JSON_SCAN_TAL(tmpctx, json_strdup, &dir),
JSON_SCAN_TAL(tmpctx, json_strdup, &network),
JSON_SCAN_TAL(p, json_to_feature_set, &p->our_features),
JSON_SCAN_TAL(p, json_strdup, &p->rpc_location));
if (err)
plugin_err(p, "cannot scan init params: %s: %.*s",
err, json_tok_full_len(params),
json_tok_full(buf, params));
/* Move into lightning directory: other files are relative */

View File

@@ -399,9 +399,9 @@ get_remote_block_height(const char *buf, const jsmntok_t *error)
u16 type;
/* Is there even a raw_message? */
if (!json_scan(buf, error, "{data:{raw_message:%}}",
JSON_SCAN_TAL(tmpctx, json_tok_bin_from_hex,
&raw_message)))
if (json_scan(tmpctx, buf, error, "{data:{raw_message:%}}",
JSON_SCAN_TAL(tmpctx, json_tok_bin_from_hex,
&raw_message)) != NULL)
return 0;
/* BOLT #4:
@@ -431,19 +431,24 @@ static struct command_result *waitsendpay_error(struct command *cmd,
struct pay_attempt *attempt = current_attempt(pc);
errcode_t code;
int failcode;
const char *err;
attempt_failed_tok(pc, "waitsendpay", buf, error);
if (!json_scan(buf, error, "{code:%}",
JSON_SCAN(json_to_errcode, &code)))
plugin_err(cmd->plugin, "waitsendpay error gave no 'code'? '%.*s'",
error->end - error->start, buf + error->start);
err = json_scan(tmpctx, buf, error, "{code:%}",
JSON_SCAN(json_to_errcode, &code));
if (err)
plugin_err(cmd->plugin, "waitsendpay error %s? '%.*s'",
err,
json_tok_full_len(error), json_tok_full(buf, error));
if (code != PAY_UNPARSEABLE_ONION) {
if (!json_scan(buf, error, "{data:{failcode:%}}",
JSON_SCAN(json_to_int, &failcode)))
plugin_err(cmd->plugin, "waitsendpay error gave no 'failcode'? '%.*s'",
error->end - error->start, buf + error->start);
err = json_scan(tmpctx, buf, error, "{data:{failcode:%}}",
JSON_SCAN(json_to_int, &failcode));
if (err)
plugin_err(cmd->plugin, "waitsendpay failcode error %s '%.*s'",
err,
json_tok_full_len(error), json_tok_full(buf, error));
}
/* Special case for WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS.
@@ -510,12 +515,15 @@ static struct command_result *waitsendpay_error(struct command *cmd,
if (failcode & NODE) {
struct node_id id;
const char *idstr;
const char *idstr, *err;
if (!json_scan(buf, error, "{data:{erring_node:%}}",
JSON_SCAN(json_to_node_id, &id)))
plugin_err(cmd->plugin, "waitsendpay error no erring_node '%.*s'",
error->end - error->start, buf + error->start);
err = json_scan(tmpctx, buf, error, "{data:{erring_node:%}}",
JSON_SCAN(json_to_node_id, &id));
if (err)
plugin_err(cmd->plugin, "waitsendpay error %s '%.*s'",
err,
json_tok_full_len(error),
json_tok_full(buf, error));
/* FIXME: Keep as node_id, don't use strings. */
idstr = node_id_to_hexstr(tmpctx, &id);
@@ -530,13 +538,17 @@ static struct command_result *waitsendpay_error(struct command *cmd,
} else {
struct short_channel_id scid;
u32 dir;
const char *scidstr;
const char *scidstr, *err;
if (!json_scan(buf, error, "{data:{erring_channel:%,erring_direction:%}}",
JSON_SCAN(json_to_short_channel_id, &scid),
JSON_SCAN(json_to_number, &dir)))
plugin_err(cmd->plugin, "waitsendpay error no erring_channel/direction '%.*s'",
error->end - error->start, buf + error->start);
err = json_scan(tmpctx, buf, error,
"{data:{erring_channel:%,erring_direction:%}}",
JSON_SCAN(json_to_short_channel_id, &scid),
JSON_SCAN(json_to_number, &dir));
if (err)
plugin_err(cmd->plugin, "waitsendpay error %s '%.*s'",
err,
json_tok_full_len(error),
json_tok_full(buf, error));
scidstr = short_channel_id_to_str(tmpctx, &scid);
/* If failure is in routehint part, try next one */
@@ -736,6 +748,7 @@ static struct command_result *getroute_done(struct command *cmd,
struct amount_msat max_fee;
u32 delay;
struct out_req *req;
const char *err;
if (!t)
plugin_err(cmd->plugin, "getroute gave no 'route'? '%.*s'",
@@ -754,11 +767,13 @@ static struct command_result *getroute_done(struct command *cmd,
} else
attempt->route = json_strdup(pc->ps->attempts, buf, t);
if (!json_scan(buf, t, "[0:{msatoshi:%,delay:%}]",
JSON_SCAN(json_to_msat, &fee),
JSON_SCAN(json_to_number, &delay)))
plugin_err(cmd->plugin, "getroute with invalid msatoshi/delay? %.*s",
result->end - result->start, buf);
err = json_scan(tmpctx, buf, t, "[0:{msatoshi:%,delay:%}]",
JSON_SCAN(json_to_msat, &fee),
JSON_SCAN(json_to_number, &delay));
if (err)
plugin_err(cmd->plugin, "getroute %s? %.*s",
err,
json_tok_full_len(result), json_tok_full(buf, result));
if (pc->maxfee_pct_millionths / 100 > UINT32_MAX)
plugin_err(cmd->plugin, "max fee percent too large: %lf",

View File

@@ -547,15 +547,17 @@ static void json_peer_sigs(struct command *cmd,
struct channel_id cid;
const struct wally_psbt *psbt;
struct multifundchannel_destination *dest;
const char *err;
if (!json_scan(buf, params,
"{openchannel_peer_sigs:"
"{channel_id:%,signed_psbt:%}}",
JSON_SCAN(json_to_channel_id, &cid),
JSON_SCAN_TAL(cmd, json_to_psbt, &psbt)))
err = json_scan(tmpctx, buf, params,
"{openchannel_peer_sigs:"
"{channel_id:%,signed_psbt:%}}",
JSON_SCAN(json_to_channel_id, &cid),
JSON_SCAN_TAL(cmd, json_to_psbt, &psbt));
if (err)
plugin_err(cmd->plugin,
"`openchannel_peer_sigs` did not scan",
json_tok_full_len(params),
"`openchannel_peer_sigs` did not scan: %s",
err, json_tok_full_len(params),
json_tok_full(buf, params));
/* Find the destination that's got this channel_id on it! */