From 7b1793f40deb92d8221bf2e79caf2cca262214da Mon Sep 17 00:00:00 2001 From: dovgopoly Date: Fri, 9 Jan 2026 17:16:44 +0200 Subject: [PATCH] lightningd: add `get_bitcoin_result` for bcli response handling Add `get_bitcoin_result` function that checks bcli plugin responses for errors and returns the result token. Previously, callbacks only detected errors when result parsing failed, ignoring the explicit error field from the plugin. Now we extract the actual error message from bcli, providing clearer reasoning when the plugin returns an error response. --- lightningd/bitcoind.c | 75 ++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/lightningd/bitcoind.c b/lightningd/bitcoind.c index 58b7b0929..c1f461f7f 100644 --- a/lightningd/bitcoind.c +++ b/lightningd/bitcoind.c @@ -112,6 +112,35 @@ bitcoin_plugin_error(struct bitcoind *bitcoind, const char *buf, toks->end - toks->start, buf + toks->start); } +/* Check response for error (fatal if found) and return result token (fatal if not found). */ +static const jsmntok_t *get_bitcoin_result(struct bitcoind *bitcoind, + const char *buf, + const jsmntok_t *toks, + const char *method) +{ + const jsmntok_t *err_tok, *msg_tok, *result_tok; + + err_tok = json_get_member(buf, toks, "error"); + if (err_tok) { + msg_tok = json_get_member(buf, err_tok, "message"); + if (msg_tok) + bitcoin_plugin_error(bitcoind, buf, toks, method, + "%.*s", json_tok_full_len(msg_tok), + json_tok_full(buf, msg_tok)); + else + bitcoin_plugin_error(bitcoind, buf, toks, method, + "%.*s", json_tok_full_len(err_tok), + json_tok_full(buf, err_tok)); + } + + result_tok = json_get_member(buf, toks, "result"); + if (!result_tok) + bitcoin_plugin_error(bitcoind, buf, toks, method, + "missing 'result' field"); + + return result_tok; +} + /* Send a request to the Bitcoin plugin which registered that method, * if it's still alive. */ static void bitcoin_plugin_send(struct bitcoind *bitcoind, @@ -227,11 +256,7 @@ static void estimatefees_callback(const char *buf, const jsmntok_t *toks, struct feerate_est *feerates; u32 floor; - resulttok = json_get_member(buf, toks, "result"); - if (!resulttok) - bitcoin_plugin_error(call->bitcoind, buf, toks, - "estimatefees", - "bad 'result' field"); + resulttok = get_bitcoin_result(call->bitcoind, buf, toks, "estimatefees"); /* Modern style has floor. */ floortok = json_get_member(buf, resulttok, "feerate_floor"); @@ -315,21 +340,24 @@ static void sendrawtx_callback(const char *buf, const jsmntok_t *toks, const jsmntok_t *idtok, struct sendrawtx_call *call) { + const jsmntok_t *resulttok; const char *err; const char *errmsg = NULL; bool success = false; - err = json_scan(tmpctx, buf, toks, "{result:{success:%}}", + resulttok = get_bitcoin_result(call->bitcoind, buf, toks, "sendrawtransaction"); + + err = json_scan(tmpctx, buf, resulttok, "{success:%}", JSON_SCAN(json_to_bool, &success)); if (err) { - bitcoin_plugin_error(call->bitcoind, buf, toks, + bitcoin_plugin_error(call->bitcoind, buf, resulttok, "sendrawtransaction", "bad 'result' field: %s", err); } else if (!success) { - err = json_scan(tmpctx, buf, toks, "{result:{errmsg:%}}", + err = json_scan(tmpctx, buf, resulttok, "{errmsg:%}", JSON_SCAN_TAL(tmpctx, json_strdup, &errmsg)); if (err) - bitcoin_plugin_error(call->bitcoind, buf, toks, + bitcoin_plugin_error(call->bitcoind, buf, resulttok, "sendrawtransaction", "bad 'errmsg' field: %s", err); @@ -394,6 +422,7 @@ getrawblockbyheight_callback(const char *buf, const jsmntok_t *toks, const jsmntok_t *idtok, struct getrawblockbyheight_call *call) { + const jsmntok_t *resulttok; const char *block_str, *err; struct bitcoin_blkid blkid; struct bitcoin_block *blk; @@ -401,6 +430,8 @@ getrawblockbyheight_callback(const char *buf, const jsmntok_t *toks, trace_span_resume(call); trace_span_end(call); + resulttok = get_bitcoin_result(call->bitcoind, buf, toks, "getrawblockbyheight"); + /* Callback may free parent of call, so steal onto context to * free if it doesn't */ ctx = tal(NULL, char); @@ -408,24 +439,24 @@ getrawblockbyheight_callback(const char *buf, const jsmntok_t *toks, /* If block hash is `null`, this means not found! Call the callback * with NULL values. */ - err = json_scan(tmpctx, buf, toks, "{result:{blockhash:null}}"); + err = json_scan(tmpctx, buf, resulttok, "{blockhash:null}"); if (!err) { call->cb(call->bitcoind, call->height, NULL, NULL, call->cb_arg); goto clean; } - err = json_scan(tmpctx, buf, toks, "{result:{blockhash:%,block:%}}", + err = json_scan(tmpctx, buf, resulttok, "{blockhash:%,block:%}", JSON_SCAN(json_to_sha256, &blkid.shad.sha), JSON_SCAN_TAL(tmpctx, json_strdup, &block_str)); if (err) - bitcoin_plugin_error(call->bitcoind, buf, toks, + bitcoin_plugin_error(call->bitcoind, buf, resulttok, "getrawblockbyheight", "bad 'result' field: %s", err); blk = bitcoin_block_from_hex(tmpctx, chainparams, block_str, strlen(block_str)); if (!blk) - bitcoin_plugin_error(call->bitcoind, buf, toks, + bitcoin_plugin_error(call->bitcoind, buf, resulttok, "getrawblockbyheight", "bad block"); @@ -494,18 +525,21 @@ static void getchaininfo_callback(const char *buf, const jsmntok_t *toks, const jsmntok_t *idtok, struct getchaininfo_call *call) { + const jsmntok_t *resulttok; const char *err, *chain; u32 headers, blocks; bool ibd; - err = json_scan(tmpctx, buf, toks, - "{result:{chain:%,headercount:%,blockcount:%,ibd:%}}", + resulttok = get_bitcoin_result(call->bitcoind, buf, toks, "getchaininfo"); + + err = json_scan(tmpctx, buf, resulttok, + "{chain:%,headercount:%,blockcount:%,ibd:%}", 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) - bitcoin_plugin_error(call->bitcoind, buf, toks, "getchaininfo", + bitcoin_plugin_error(call->bitcoind, buf, resulttok, "getchaininfo", "bad 'result' field: %s", err); call->cb(call->bitcoind, chain, headers, blocks, ibd, @@ -565,24 +599,27 @@ static void getutxout_callback(const char *buf, const jsmntok_t *toks, const jsmntok_t *idtok, struct getutxout_call *call) { + const jsmntok_t *resulttok; const char *err; struct bitcoin_tx_output txout; /* Whatever happens, we want to free this. */ tal_steal(tmpctx, call); - err = json_scan(tmpctx, buf, toks, "{result:{script:null}}"); + resulttok = get_bitcoin_result(call->bitcoind, buf, toks, "getutxout"); + + err = json_scan(tmpctx, buf, resulttok, "{script:null}"); if (!err) { call->cb(call->bitcoind, NULL, call->cb_arg); return; } - err = json_scan(tmpctx, buf, toks, "{result:{script:%,amount:%}}", + err = json_scan(tmpctx, buf, resulttok, "{script:%,amount:%}", JSON_SCAN_TAL(tmpctx, json_tok_bin_from_hex, &txout.script), JSON_SCAN(json_to_sat, &txout.amount)); if (err) - bitcoin_plugin_error(call->bitcoind, buf, toks, "getutxout", + bitcoin_plugin_error(call->bitcoind, buf, resulttok, "getutxout", "bad 'result' field: %s", err); call->cb(call->bitcoind, &txout, call->cb_arg);