From c79ab0f1b0787c528ba2f7b64d3e2f349200e01f Mon Sep 17 00:00:00 2001 From: darosior Date: Thu, 9 Jan 2020 16:38:12 +0100 Subject: [PATCH] lightningd/bitcoind: use the Bitcoin plugin for getutxout --- lightningd/bitcoind.c | 139 ++++++++++---------- lightningd/bitcoind.h | 19 ++- lightningd/channel_control.c | 10 +- lightningd/peer_control.c | 8 +- lightningd/test/run-invoice-select-inchan.c | 26 ++-- wallet/test/run-wallet.c | 54 ++++---- wallet/wallet.c | 8 +- wallet/walletrpc.c | 4 +- 8 files changed, 137 insertions(+), 131 deletions(-) diff --git a/lightningd/bitcoind.c b/lightningd/bitcoind.c index d114c92fa..46f8c666c 100644 --- a/lightningd/bitcoind.c +++ b/lightningd/bitcoind.c @@ -781,89 +781,88 @@ void bitcoind_getchaininfo_(struct bitcoind *bitcoind, req); } -struct get_output { +/* `getutxout` + * + * Get informations about an UTXO. If the TXO is spent, the plugin will set + * all fields to `null`. + * { + * "amount": , + * "script": "The output's scriptPubKey", + * } + */ + +struct getutxout_call { + struct bitcoind *bitcoind; unsigned int blocknum, txnum, outnum; /* The real callback */ - void (*cb)(struct bitcoind *bitcoind, const struct bitcoin_tx_output *txout, void *arg); - + void (*cb)(struct bitcoind *bitcoind, + const struct bitcoin_tx_output *txout, void *arg); /* The real callback arg */ - void *cbarg; + void *cb_arg; }; -static bool process_gettxout(struct bitcoin_cli *bcli) +static void getutxout_callback(const char *buf, const jsmntok_t *toks, + const jsmntok_t *idtok, + struct getutxout_call *call) { - void (*cb)(struct bitcoind *bitcoind, - const struct bitcoin_tx_output *output, - void *arg) = bcli->cb; - const jsmntok_t *tokens, *valuetok, *scriptpubkeytok, *hextok; - struct bitcoin_tx_output out; - bool valid; + const jsmntok_t *resulttok, *amounttok, *scripttok; + struct bitcoin_tx_output txout; - /* As of at least v0.15.1.0, bitcoind returns "success" but an empty - string on a spent gettxout */ - if (*bcli->exitstatus != 0 || bcli->output_bytes == 0) { - cb(bcli->bitcoind, NULL, bcli->cb_arg); - return true; - } - - tokens = json_parse_input(bcli->output, bcli->output, bcli->output_bytes, - &valid); - if (!tokens) - fatal("%s: %s response", - bcli_args(tmpctx, bcli), valid ? "partial" : "invalid"); - - if (tokens[0].type != JSMN_OBJECT) - fatal("%s: gave non-object (%.*s)?", - bcli_args(tmpctx, bcli), - (int)bcli->output_bytes, bcli->output); - - valuetok = json_get_member(bcli->output, tokens, "value"); - if (!valuetok) - fatal("%s: had no value member (%.*s)?", - bcli_args(tmpctx, bcli), - (int)bcli->output_bytes, bcli->output); + resulttok = json_get_member(buf, toks, "result"); + if (!resulttok) + bitcoin_plugin_error(call->bitcoind, buf, toks, "getutxout", + "bad 'result' field"); - if (!json_to_bitcoin_amount(bcli->output, valuetok, &out.amount.satoshis)) /* Raw: talking to bitcoind */ - fatal("%s: had bad value (%.*s)?", - bcli_args(tmpctx, bcli), - (int)bcli->output_bytes, bcli->output); + scripttok = json_get_member(buf, resulttok, "script"); + if (!scripttok) + bitcoin_plugin_error(call->bitcoind, buf, toks, "getutxout", + "bad 'script' field"); + if (json_tok_is_null(buf, scripttok)) { + db_begin_transaction(call->bitcoind->ld->wallet->db); + call->cb(call->bitcoind, NULL, call->cb_arg); + db_commit_transaction(call->bitcoind->ld->wallet->db); + goto clean; + } + txout.script = json_tok_bin_from_hex(tmpctx, buf, scripttok); - scriptpubkeytok = json_get_member(bcli->output, tokens, "scriptPubKey"); - if (!scriptpubkeytok) - fatal("%s: had no scriptPubKey member (%.*s)?", - bcli_args(tmpctx, bcli), - (int)bcli->output_bytes, bcli->output); - hextok = json_get_member(bcli->output, scriptpubkeytok, "hex"); - if (!hextok) - fatal("%s: had no scriptPubKey->hex member (%.*s)?", - bcli_args(tmpctx, bcli), - (int)bcli->output_bytes, bcli->output); + amounttok = json_get_member(buf, resulttok, "amount"); + if (!amounttok) + bitcoin_plugin_error(call->bitcoind, buf, toks, "getutxout", + "bad 'amount' field"); + if (!json_to_sat(buf, amounttok, &txout.amount)) + bitcoin_plugin_error(call->bitcoind, buf, toks, "getutxout", + "bad sats amount"); - out.script = tal_hexdata(bcli, bcli->output + hextok->start, - hextok->end - hextok->start); - if (!out.script) - fatal("%s: scriptPubKey->hex invalid hex (%.*s)?", - bcli_args(tmpctx, bcli), - (int)bcli->output_bytes, bcli->output); + db_begin_transaction(call->bitcoind->ld->wallet->db); + call->cb(call->bitcoind, &txout, call->cb_arg); + db_commit_transaction(call->bitcoind->ld->wallet->db); - cb(bcli->bitcoind, &out, bcli->cb_arg); - return true; +clean: + tal_free(call); } -void bitcoind_gettxout(struct bitcoind *bitcoind, - const struct bitcoin_txid *txid, const u32 outnum, - void (*cb)(struct bitcoind *bitcoind, - const struct bitcoin_tx_output *txout, - void *arg), - void *arg) +void bitcoind_getutxout_(struct bitcoind *bitcoind, + const struct bitcoin_txid *txid, const u32 outnum, + void (*cb)(struct bitcoind *bitcoind, + const struct bitcoin_tx_output *txout, + void *arg), + void *cb_arg) { - start_bitcoin_cli(bitcoind, NULL, - process_gettxout, true, BITCOIND_LOW_PRIO, cb, arg, - "gettxout", - take(type_to_string(NULL, struct bitcoin_txid, txid)), - take(tal_fmt(NULL, "%u", outnum)), - NULL); + struct jsonrpc_request *req; + struct getutxout_call *call = tal(bitcoind, struct getutxout_call); + + call->bitcoind = bitcoind; + call->cb = cb; + call->cb_arg = cb_arg; + + req = jsonrpc_request_start(bitcoind, "getutxout", bitcoind->log, + getutxout_callback, call); + json_add_txid(req->stream, "txid", txid); + json_add_num(req->stream, "vout", outnum); + jsonrpc_request_end(req); + plugin_request_send(strmap_get(&bitcoind->pluginsmap, "getutxout"), + req); } /* Context for the getfilteredblock call. Wraps the actual arguments while we @@ -901,7 +900,7 @@ process_getfilteredblock_step2(struct bitcoind *bitcoind, call->current_outpoint++; if (call->current_outpoint < tal_count(call->outpoints)) { o = call->outpoints[call->current_outpoint]; - bitcoind_gettxout(bitcoind, &o->txid, o->outnum, + bitcoind_getutxout(bitcoind, &o->txid, o->outnum, process_getfilteredblock_step2, call); } else { /* If there were no more outpoints to check, we call the callback. */ @@ -969,7 +968,7 @@ static void process_getfilteredblock_step1(struct bitcoind *bitcoind, * store the one's that are unspent in * call->result->outpoints. */ o = call->outpoints[call->current_outpoint]; - bitcoind_gettxout(bitcoind, &o->txid, o->outnum, + bitcoind_getutxout(bitcoind, &o->txid, o->outnum, process_getfilteredblock_step2, call); } } diff --git a/lightningd/bitcoind.h b/lightningd/bitcoind.h index c71eabf6e..97f6f7bb2 100644 --- a/lightningd/bitcoind.h +++ b/lightningd/bitcoind.h @@ -166,12 +166,19 @@ void bitcoind_getrawblockbyheight_(struct bitcoind *bitcoind, struct bitcoin_block *),\ (arg)) -void bitcoind_gettxout(struct bitcoind *bitcoind, - const struct bitcoin_txid *txid, const u32 outnum, - void (*cb)(struct bitcoind *bitcoind, - const struct bitcoin_tx_output *txout, - void *arg), - void *arg); +void bitcoind_getutxout_(struct bitcoind *bitcoind, + const struct bitcoin_txid *txid, const u32 outnum, + void (*cb)(struct bitcoind *bitcoind, + const struct bitcoin_tx_output *txout, + void *arg), + void *arg); +#define bitcoind_getutxout(bitcoind_, txid_, vout_, cb, arg) \ + bitcoind_getutxout_((bitcoind_), (txid_), (vout_), \ + typesafe_cb_preargs(void, void *, \ + (cb), (arg), \ + struct bitcoind *, \ + struct bitcoin_tx_output *),\ + (arg)) void bitcoind_getclientversion(struct bitcoind *bitcoind); diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index f779672b6..7a3bd3479 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -774,11 +774,11 @@ struct command_result *cancel_channel_before_broadcast(struct command *cmd, /* Note: The above check and this check can't completely ensure that * the funding transaction isn't broadcast. We can't know if the funding * is broadcast by external wallet and the transaction hasn't been onchain. */ - bitcoind_gettxout(cmd->ld->topology->bitcoind, - &cancel_channel->funding_txid, - cancel_channel->funding_outnum, - process_check_funding_broadcast, - notleak(cc)); + bitcoind_getutxout(cmd->ld->topology->bitcoind, + &cancel_channel->funding_txid, + cancel_channel->funding_outnum, + process_check_funding_broadcast, + notleak(cc)); return command_still_pending(cmd); } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 2e2fdd9de..957e3f9d6 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2266,10 +2266,10 @@ static struct command_result *json_dev_forget_channel(struct command *cmd, "or `dev-fail` instead."); } - bitcoind_gettxout(cmd->ld->topology->bitcoind, - &forget->channel->funding_txid, - forget->channel->funding_outnum, - process_dev_forget_channel, forget); + bitcoind_getutxout(cmd->ld->topology->bitcoind, + &forget->channel->funding_txid, + forget->channel->funding_outnum, + process_dev_forget_channel, forget); return command_still_pending(cmd); } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index bd44eccef..496bb164f 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -13,14 +13,14 @@ size_t bigsize_get(const u8 *p UNNEEDED, size_t max UNNEEDED, bigsize_t *val UNN /* Generated stub for bigsize_put */ size_t bigsize_put(u8 buf[BIGSIZE_MAX_LEN] UNNEEDED, bigsize_t v UNNEEDED) { fprintf(stderr, "bigsize_put called!\n"); abort(); } -/* Generated stub for bitcoind_gettxout */ -void bitcoind_gettxout(struct bitcoind *bitcoind UNNEEDED, - const struct bitcoin_txid *txid UNNEEDED, const u32 outnum UNNEEDED, - void (*cb)(struct bitcoind *bitcoind UNNEEDED, - const struct bitcoin_tx_output *txout UNNEEDED, - void *arg) UNNEEDED, - void *arg UNNEEDED) -{ fprintf(stderr, "bitcoind_gettxout called!\n"); abort(); } +/* Generated stub for bitcoind_getutxout_ */ +void bitcoind_getutxout_(struct bitcoind *bitcoind UNNEEDED, + const struct bitcoin_txid *txid UNNEEDED, const u32 outnum UNNEEDED, + void (*cb)(struct bitcoind *bitcoind UNNEEDED, + const struct bitcoin_tx_output *txout UNNEEDED, + void *arg) UNNEEDED, + void *arg UNNEEDED) +{ fprintf(stderr, "bitcoind_getutxout_ called!\n"); abort(); } /* Generated stub for bolt11_decode */ struct bolt11 *bolt11_decode(const tal_t *ctx UNNEEDED, const char *str UNNEEDED, const char *description UNNEEDED, char **fail UNNEEDED) @@ -90,7 +90,7 @@ struct fee_states *dup_fee_states(const tal_t *ctx UNNEEDED, /* Generated stub for encode_scriptpubkey_to_addr */ char *encode_scriptpubkey_to_addr(const tal_t *ctx UNNEEDED, const struct chainparams *chainparams UNNEEDED, - const u8 *scriptPubkey UNNEEDED) + const u8 *scriptPubkey UNNEEDED) { fprintf(stderr, "encode_scriptpubkey_to_addr called!\n"); abort(); } /* Generated stub for fatal */ void fatal(const char *fmt UNNEEDED, ...) @@ -198,6 +198,10 @@ enum address_parse_result json_to_address_scriptpubkey(const tal_t *ctx UNNEEDED const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, const u8 **scriptpubkey UNNEEDED) { fprintf(stderr, "json_to_address_scriptpubkey called!\n"); abort(); } +/* Generated stub for json_tok_channel_id */ +bool json_tok_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct channel_id *cid UNNEEDED) +{ fprintf(stderr, "json_tok_channel_id called!\n"); abort(); } /* Generated stub for json_to_node_id */ bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct node_id *id UNNEEDED) @@ -206,10 +210,6 @@ bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct short_channel_id *scid UNNEEDED) { fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } -/* Generated stub for json_tok_channel_id */ -bool json_tok_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct channel_id *cid UNNEEDED) -{ fprintf(stderr, "json_tok_channel_id called!\n"); abort(); } /* Generated stub for kill_uncommitted_channel */ void kill_uncommitted_channel(struct uncommitted_channel *uc UNNEEDED, const char *why UNNEEDED) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 0f705cc38..d870e0b74 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -37,14 +37,14 @@ size_t bigsize_get(const u8 *p UNNEEDED, size_t max UNNEEDED, bigsize_t *val UNN /* Generated stub for bigsize_put */ size_t bigsize_put(u8 buf[BIGSIZE_MAX_LEN] UNNEEDED, bigsize_t v UNNEEDED) { fprintf(stderr, "bigsize_put called!\n"); abort(); } -/* Generated stub for bitcoind_gettxout */ -void bitcoind_gettxout(struct bitcoind *bitcoind UNNEEDED, - const struct bitcoin_txid *txid UNNEEDED, const u32 outnum UNNEEDED, - void (*cb)(struct bitcoind *bitcoind UNNEEDED, - const struct bitcoin_tx_output *txout UNNEEDED, - void *arg) UNNEEDED, - void *arg UNNEEDED) -{ fprintf(stderr, "bitcoind_gettxout called!\n"); abort(); } +/* Generated stub for bitcoind_getutxout_ */ +void bitcoind_getutxout_(struct bitcoind *bitcoind UNNEEDED, + const struct bitcoin_txid *txid UNNEEDED, const u32 outnum UNNEEDED, + void (*cb)(struct bitcoind *bitcoind UNNEEDED, + const struct bitcoin_tx_output *txout UNNEEDED, + void *arg) UNNEEDED, + void *arg UNNEEDED) +{ fprintf(stderr, "bitcoind_getutxout_ called!\n"); abort(); } /* Generated stub for broadcast_tx */ void broadcast_tx(struct chain_topology *topo UNNEEDED, struct channel *channel UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, @@ -89,7 +89,7 @@ void delay_then_reconnect(struct channel *channel UNNEEDED, u32 seconds_delay UN /* Generated stub for encode_scriptpubkey_to_addr */ char *encode_scriptpubkey_to_addr(const tal_t *ctx UNNEEDED, const struct chainparams *chainparams UNNEEDED, - const u8 *scriptPubkey UNNEEDED) + const u8 *scriptPubkey UNNEEDED) { fprintf(stderr, "encode_scriptpubkey_to_addr called!\n"); abort(); } /* Generated stub for fatal */ void fatal(const char *fmt UNNEEDED, ...) @@ -343,21 +343,6 @@ enum address_parse_result json_to_address_scriptpubkey(const tal_t *ctx UNNEEDED /* Generated stub for json_to_bool */ bool json_to_bool(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool *b UNNEEDED) { fprintf(stderr, "json_to_bool called!\n"); abort(); } -/* Generated stub for json_to_node_id */ -bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct node_id *id UNNEEDED) -{ fprintf(stderr, "json_to_node_id called!\n"); abort(); } -/* Generated stub for json_to_number */ -bool json_to_number(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - unsigned int *num UNNEEDED) -{ fprintf(stderr, "json_to_number called!\n"); abort(); } -/* Generated stub for json_to_preimage */ -bool json_to_preimage(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct preimage *preimage UNNEEDED) -{ fprintf(stderr, "json_to_preimage called!\n"); abort(); } -/* Generated stub for json_to_short_channel_id */ -bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct short_channel_id *scid UNNEEDED) -{ fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } /* Generated stub for json_tok_bin_from_hex */ u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) { fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } @@ -374,6 +359,21 @@ int json_tok_full_len(const jsmntok_t *t UNNEEDED) /* Generated stub for json_tok_streq */ bool json_tok_streq(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, const char *str UNNEEDED) { fprintf(stderr, "json_tok_streq called!\n"); abort(); } +/* Generated stub for json_to_node_id */ +bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct node_id *id UNNEEDED) +{ fprintf(stderr, "json_to_node_id called!\n"); abort(); } +/* Generated stub for json_to_number */ +bool json_to_number(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + unsigned int *num UNNEEDED) +{ fprintf(stderr, "json_to_number called!\n"); abort(); } +/* Generated stub for json_to_preimage */ +bool json_to_preimage(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct preimage *preimage UNNEEDED) +{ fprintf(stderr, "json_to_preimage called!\n"); abort(); } +/* Generated stub for json_to_short_channel_id */ +bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } /* Generated stub for kill_uncommitted_channel */ void kill_uncommitted_channel(struct uncommitted_channel *uc UNNEEDED, const char *why UNNEEDED) @@ -587,12 +587,12 @@ u8 *towire_channel_got_revoke_reply(const tal_t *ctx UNNEEDED) /* Generated stub for towire_channel_offer_htlc */ u8 *towire_channel_offer_htlc(const tal_t *ctx UNNEEDED, struct amount_msat amount_msat UNNEEDED, u32 cltv_expiry UNNEEDED, const struct sha256 *payment_hash UNNEEDED, const u8 onion_routing_packet[1366]) { fprintf(stderr, "towire_channel_offer_htlc called!\n"); abort(); } -/* Generated stub for towire_channel_send_shutdown */ -u8 *towire_channel_send_shutdown(const tal_t *ctx UNNEEDED, const u8 *shutdown_scriptpubkey UNNEEDED) -{ fprintf(stderr, "towire_channel_send_shutdown called!\n"); abort(); } /* Generated stub for towire_channel_sending_commitsig_reply */ u8 *towire_channel_sending_commitsig_reply(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channel_sending_commitsig_reply called!\n"); abort(); } +/* Generated stub for towire_channel_send_shutdown */ +u8 *towire_channel_send_shutdown(const tal_t *ctx UNNEEDED, const u8 *shutdown_scriptpubkey UNNEEDED) +{ fprintf(stderr, "towire_channel_send_shutdown called!\n"); abort(); } /* Generated stub for towire_channel_specific_feerates */ u8 *towire_channel_specific_feerates(const tal_t *ctx UNNEEDED, u32 feerate_base UNNEEDED, u32 feerate_ppm UNNEEDED) { fprintf(stderr, "towire_channel_specific_feerates called!\n"); abort(); } diff --git a/wallet/wallet.c b/wallet/wallet.c index 05ff3974a..c83e0d663 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -3599,8 +3599,8 @@ static void process_utxo_result(struct bitcoind *bitcoind, /* If we have more, resolve them too. */ tal_arr_remove(&utxos, 0); if (tal_count(utxos) != 0) { - bitcoind_gettxout(bitcoind, &utxos[0]->txid, utxos[0]->outnum, - process_utxo_result, utxos); + bitcoind_getutxout(bitcoind, &utxos[0]->txid, utxos[0]->outnum, + process_utxo_result, utxos); } else tal_free(utxos); } @@ -3610,8 +3610,8 @@ void wallet_clean_utxos(struct wallet *w, struct bitcoind *bitcoind) struct utxo **utxos = wallet_get_utxos(NULL, w, output_state_reserved); if (tal_count(utxos) != 0) { - bitcoind_gettxout(bitcoind, &utxos[0]->txid, utxos[0]->outnum, - process_utxo_result, notleak(utxos)); + bitcoind_getutxout(bitcoind, &utxos[0]->txid, utxos[0]->outnum, + process_utxo_result, notleak(utxos)); } else tal_free(utxos); } diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 311a6569e..d5b75dcfb 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -930,7 +930,7 @@ static void process_utxo_result(struct bitcoind *bitcoind, json_array_end(rescan->response); was_pending(command_success(rescan->cmd, rescan->response)); } else { - bitcoind_gettxout( + bitcoind_getutxout( bitcoind->ld->topology->bitcoind, &rescan->utxos[0]->txid, rescan->utxos[0]->outnum, process_utxo_result, rescan); } @@ -956,7 +956,7 @@ static struct command_result *json_dev_rescan_outputs(struct command *cmd, json_array_end(rescan->response); return command_success(cmd, rescan->response); } - bitcoind_gettxout(cmd->ld->topology->bitcoind, &rescan->utxos[0]->txid, + bitcoind_getutxout(cmd->ld->topology->bitcoind, &rescan->utxos[0]->txid, rescan->utxos[0]->outnum, process_utxo_result, rescan); return command_still_pending(cmd);