diff --git a/common/json.c b/common/json.c index 4b84c0220661..2c7fac185f1c 100644 --- a/common/json.c +++ b/common/json.c @@ -830,44 +830,11 @@ void json_add_time(struct json_stream *result, const char *fieldname, void json_add_tok(struct json_stream *result, const char *fieldname, const jsmntok_t *tok, const char *buffer) { - int i = 0; - const jsmntok_t *t; - - switch (tok->type) { - case JSMN_PRIMITIVE: - if (json_tok_is_num(buffer, tok)) { - json_to_int(buffer, tok, &i); - json_add_num(result, fieldname, i); - } - return; - - case JSMN_STRING: - if (json_tok_streq(buffer, tok, "true")) - json_add_bool(result, fieldname, true); - else if (json_tok_streq(buffer, tok, "false")) - json_add_bool(result, fieldname, false); - else - json_add_string(result, fieldname, json_strdup(tmpctx, buffer, tok)); - return; + char *space; + assert(tok->type != JSMN_UNDEFINED); - case JSMN_ARRAY: - json_array_start(result, fieldname); - json_for_each_arr(i, t, tok) - json_add_tok(result, NULL, t, buffer); - json_array_end(result); - return; - - case JSMN_OBJECT: - json_object_start(result, fieldname); - json_for_each_obj(i, t, tok) - json_add_tok(result, json_strdup(tmpctx, buffer, t), t+1, buffer); - json_object_end(result); - return; - - case JSMN_UNDEFINED: - break; - } - abort(); + space = json_member_direct(result, fieldname, json_tok_full_len(tok)); + memcpy(space, json_tok_full(buffer, tok), json_tok_full_len(tok)); } void json_add_errcode(struct json_stream *result, const char *fieldname, diff --git a/plugins/.gitignore b/plugins/.gitignore index 4a5a3c669c3d..7009ca1047f2 100644 --- a/plugins/.gitignore +++ b/plugins/.gitignore @@ -1,6 +1,5 @@ autoclean bcli -fundchannel pay spenderp multifundchannel diff --git a/plugins/Makefile b/plugins/Makefile index 36607ff16f99..36112b850656 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -4,9 +4,6 @@ PLUGIN_PAY_OBJS := $(PLUGIN_PAY_SRC:.c=.o) PLUGIN_AUTOCLEAN_SRC := plugins/autoclean.c PLUGIN_AUTOCLEAN_OBJS := $(PLUGIN_AUTOCLEAN_SRC:.c=.o) -PLUGIN_FUNDCHANNEL_SRC := plugins/fundchannel.c -PLUGIN_FUNDCHANNEL_OBJS := $(PLUGIN_FUNDCHANNEL_SRC:.c=.o) - PLUGIN_TXPREPARE_SRC := plugins/txprepare.c PLUGIN_TXPREPARE_OBJS := $(PLUGIN_TXPREPARE_SRC:.c=.o) @@ -24,24 +21,24 @@ PLUGIN_PAY_LIB_SRC := plugins/libplugin-pay.c PLUGIN_PAY_LIB_HEADER := plugins/libplugin-pay.h PLUGIN_PAY_LIB_OBJS := $(PLUGIN_PAY_LIB_SRC:.c=.o) -PLUGIN_MULTIFUNDCHANNEL_SRC := plugins/multifundchannel.c -PLUGIN_MULTIFUNDCHANNEL_OBJS := $(PLUGIN_MULTIFUNDCHANNEL_SRC:.c=.o) - PLUGIN_SPENDER_SRC := \ + plugins/spender/fundchannel.c \ plugins/spender/main.c \ + plugins/spender/multifundchannel.c \ plugins/spender/multiwithdraw.c PLUGIN_SPENDER_HEADER := \ - plugins/spender/multiwithdraw.h + plugins/spender/multifundchannel.h \ + plugins/spender/multiwithdraw.h \ + plugins/spender/fundchannel.h \ + plugins/spender/multifundchannel.h PLUGIN_SPENDER_OBJS := $(PLUGIN_SPENDER_SRC:.c=.o) PLUGIN_ALL_SRC := \ $(PLUGIN_AUTOCLEAN_SRC) \ $(PLUGIN_BCLI_SRC) \ - $(PLUGIN_FUNDCHANNEL_SRC) \ $(PLUGIN_KEYSEND_SRC) \ $(PLUGIN_TXPREPARE_SRC) \ $(PLUGIN_LIB_SRC) \ - $(PLUGIN_MULTIFUNDCHANNEL_SRC) \ $(PLUGIN_PAY_LIB_SRC) \ $(PLUGIN_PAY_SRC) \ $(PLUGIN_SPENDER_SRC) @@ -54,10 +51,8 @@ PLUGIN_ALL_OBJS := $(PLUGIN_ALL_SRC:.c=.o) PLUGINS := \ plugins/autoclean \ plugins/bcli \ - plugins/fundchannel \ plugins/keysend \ plugins/pay \ - plugins/multifundchannel \ plugins/txprepare \ plugins/spenderp @@ -111,8 +106,6 @@ $(PLUGIN_PAY_OBJS): $(PLUGIN_PAY_LIB_HEADER) plugins/autoclean: bitcoin/chainparams.o $(PLUGIN_AUTOCLEAN_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) -plugins/fundchannel: common/addr.o $(PLUGIN_FUNDCHANNEL_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) - plugins/txprepare: bitcoin/chainparams.o $(PLUGIN_TXPREPARE_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) plugins/bcli: bitcoin/chainparams.o $(PLUGIN_BCLI_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) @@ -120,8 +113,6 @@ plugins/bcli: bitcoin/chainparams.o $(PLUGIN_BCLI_OBJS) $(PLUGIN_LIB_OBJS) $(PLU plugins/keysend: bitcoin/chainparams.o wire/tlvstream.o wire/onion$(EXP)_wiregen.o $(PLUGIN_KEYSEND_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(PLUGIN_KEYSEND_OBJS): $(PLUGIN_PAY_LIB_HEADER) -plugins/multifundchannel: bitcoin/chainparams.o common/addr.o $(PLUGIN_MULTIFUNDCHANNEL_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) - plugins/spenderp: bitcoin/chainparams.o $(PLUGIN_SPENDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(PLUGIN_ALL_OBJS): $(PLUGIN_LIB_HEADER) diff --git a/plugins/fundchannel.c b/plugins/fundchannel.c deleted file mode 100644 index a794dbe9b4c2..000000000000 --- a/plugins/fundchannel.c +++ /dev/null @@ -1,452 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -const char *placeholder_script = "0020b95810f824f843934fa042acd0becba52087813e260edaeebc42b5cb9abe1464"; -const char *placeholder_funding_addr; - -/* Populated by libplugin */ -extern const struct chainparams *chainparams; - -struct funding_req { - struct node_id *id; - const char *feerate_str; - const char *funding_str; - const char *utxo_str; - bool funding_all; - struct amount_msat *push_msat; - - /* Features offered by this peer. */ - const u8 *their_features; - - bool *announce_channel; - u32 *minconf; - - /* The prepared tx id */ - struct bitcoin_txid tx_id; - u32 outnum; - - const char *chanstr; - const u8 *out_script; - const char *funding_addr; - - /* Failing result (NULL on success) */ - /* Raw JSON from RPC output */ - const char *error; -}; - -static struct command_result *send_prior(struct command *cmd, - const char *buf, - const jsmntok_t *error, - struct funding_req *fr) -{ - return command_err_raw(cmd, fr->error); -} - -static struct command_result *tx_abort(struct command *cmd, - const char *buf, - const jsmntok_t *error, - struct funding_req *fr) -{ - struct out_req *req; - - /* We stash the error so we can return it after we've cleaned up */ - fr->error = json_strdup(fr, buf, error); - - req = jsonrpc_request_start(cmd->plugin, cmd, "txdiscard", - send_prior, send_prior, fr); - json_add_string(req->js, "txid", - type_to_string(tmpctx, struct bitcoin_txid, &fr->tx_id)); - - /* We need to call txdiscard, and forward the actual cause for the - * error after we've cleaned up. We swallow any errors returned by - * this call, as we don't really care if it succeeds or not */ - return send_outreq(cmd->plugin, req); -} - -/* We're basically done, we just need to format the output to match - * what the original `fundchannel` returned */ -static struct command_result *finish(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct funding_req *fr) -{ - struct json_stream *out; - - out = jsonrpc_stream_success(cmd); - json_add_tok(out, "tx", json_get_member(buf, result, "tx"), buf); - json_add_string(out, "txid", - type_to_string(tmpctx, struct bitcoin_txid, &fr->tx_id)); - json_add_u32(out, "outnum", fr->outnum); - json_add_string(out, "channel_id", fr->chanstr); - - return command_finished(cmd, out); -} - -/* We're ready to broadcast the transaction */ -static struct command_result *send_tx(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct funding_req *fr) -{ - - struct out_req *req; - const jsmntok_t *tok; - bool commitments_secured; - - /* For sanity's sake, let's check that it's secured */ - tok = json_get_member(buf, result, "commitments_secured"); - if (!json_to_bool(buf, tok, &commitments_secured) || !commitments_secured) - /* TODO: better failure path? this should never fail though. */ - plugin_err(cmd->plugin, "Commitment not secured."); - - /* Stash the channel_id so we can return it when finalized */ - tok = json_get_member(buf, result, "channel_id"); - fr->chanstr = json_strdup(fr, buf, tok); - - req = jsonrpc_request_start(cmd->plugin, cmd, "txsend", - finish, tx_abort, fr); - json_add_string(req->js, "txid", - type_to_string(tmpctx, struct bitcoin_txid, &fr->tx_id)); - - return send_outreq(cmd->plugin, req); -} - -static struct command_result *tx_prepare_done(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct funding_req *fr) -{ - const jsmntok_t *txid_tok; - const jsmntok_t *tx_tok; - struct out_req *req; - const struct bitcoin_tx *tx; - const char *hex; - bool outnum_found; - - txid_tok = json_get_member(buf, result, "txid"); - if (!txid_tok) - plugin_err(cmd->plugin, "txprepare missing 'txid' field"); - - tx_tok = json_get_member(buf, result, "unsigned_tx"); - if (!tx_tok) - plugin_err(cmd->plugin, "txprepare missing 'unsigned_tx' field"); - - hex = json_strdup(tmpctx, buf, tx_tok); - tx = bitcoin_tx_from_hex(fr, hex, strlen(hex)); - if (!tx) - plugin_err(cmd->plugin, "Unable to parse tx %s", hex); - - /* Find the txout */ - outnum_found = false; - for (size_t i = 0; i < tx->wtx->num_outputs; i++) { - const u8 *output_script = bitcoin_tx_output_get_script(fr, tx, i); - if (scripteq(output_script, fr->out_script)) { - fr->outnum = i; - outnum_found = true; - break; - } - } - if (!outnum_found) - plugin_err(cmd->plugin, "txprepare doesn't include our funding output. " - "tx: %s, output: %s", - type_to_string(tmpctx, struct bitcoin_tx, tx), - tal_hex(tmpctx, fr->out_script)); - - hex = json_strdup(tmpctx, buf, txid_tok); - if (!bitcoin_txid_from_hex(hex, strlen(hex), &fr->tx_id)) - plugin_err(cmd->plugin, "Unable to parse txid %s", hex); - - req = jsonrpc_request_start(cmd->plugin, cmd, "fundchannel_complete", - send_tx, tx_abort, fr); - json_add_string(req->js, "id", node_id_to_hexstr(tmpctx, fr->id)); - /* Note that hex is reused from above */ - json_add_string(req->js, "txid", hex); - json_add_u32(req->js, "txout", fr->outnum); - - return send_outreq(cmd->plugin, req); -} - -static struct command_result *cancel_start(struct command *cmd, - const char *buf, - const jsmntok_t *error, - struct funding_req *fr) -{ - struct out_req *req; - - /* We stash the error so we can return it after we've cleaned up */ - fr->error = json_strdup(fr, buf, error); - - req = jsonrpc_request_start(cmd->plugin, cmd, "fundchannel_cancel", - send_prior, send_prior, fr); - json_add_string(req->js, "id", node_id_to_hexstr(tmpctx, fr->id)); - - return send_outreq(cmd->plugin, req); -} - -static void txprepare(struct json_stream *js, - struct funding_req *fr, - const char *destination) -{ - /* Add the 'outputs' */ - json_array_start(js, "outputs"); - json_object_start(js, NULL); - json_add_string(js, destination, fr->funding_str); - json_object_end(js); - json_array_end(js); - - if (fr->feerate_str) - json_add_string(js, "feerate", fr->feerate_str); - if (fr->minconf) - json_add_u32(js, "minconf", *fr->minconf); - if (fr->utxo_str) - json_add_jsonstr(js, "utxos", fr->utxo_str); -} - -static struct command_result *prepare_actual(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct funding_req *fr) -{ - struct out_req *req; - - req = jsonrpc_request_start(cmd->plugin, cmd, "txprepare", - tx_prepare_done, cancel_start, - fr); - txprepare(req->js, fr, fr->funding_addr); - - return send_outreq(cmd->plugin, req); -} - -static struct command_result *fundchannel_start_done(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct funding_req *fr) -{ - struct out_req *req; - - /* Save the outscript so we can fund the outnum later */ - fr->out_script = json_tok_bin_from_hex(fr, buf, - json_get_member(buf, result, "scriptpubkey")); - - /* Save the funding address, we'll need it later */ - fr->funding_addr = json_strdup(cmd, buf, - json_get_member(buf, result, "funding_address")); - - /* Now that we're ready to go, cancel the reserved tx */ - req = jsonrpc_request_start(cmd->plugin, cmd, "txdiscard", - prepare_actual, cancel_start, - fr); - json_add_string(req->js, "txid", - type_to_string(tmpctx, struct bitcoin_txid, &fr->tx_id)); - - return send_outreq(cmd->plugin, req); -} - -static struct command_result *fundchannel_start(struct command *cmd, - struct funding_req *fr) -{ - struct out_req *req = jsonrpc_request_start(cmd->plugin, cmd, - "fundchannel_start", - fundchannel_start_done, - tx_abort, fr); - - json_add_string(req->js, "id", node_id_to_hexstr(tmpctx, fr->id)); - - json_add_string(req->js, "amount", fr->funding_str); - - if (fr->feerate_str) - json_add_string(req->js, "feerate", fr->feerate_str); - if (fr->announce_channel) - json_add_bool(req->js, "announce", *fr->announce_channel); - if (fr->push_msat) - json_add_string(req->js, "push_msat", - type_to_string(tmpctx, struct amount_msat, fr->push_msat)); - - return send_outreq(cmd->plugin, req); -} - -static struct command_result *post_dryrun(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct funding_req *fr) -{ - struct bitcoin_tx *tx; - const char *hex; - struct amount_sat funding; - bool funding_found; - u8 *placeholder = tal_hexdata(tmpctx, placeholder_script, strlen(placeholder_script)); - struct amount_asset asset; - - /* Stash the 'reserved' txid to unreserve later */ - hex = json_strdup(tmpctx, buf, json_get_member(buf, result, "txid")); - if (!bitcoin_txid_from_hex(hex, strlen(hex), &fr->tx_id)) - plugin_err(cmd->plugin, "Unable to parse reserved txid %s", hex); - - - hex = json_strdup(tmpctx, buf, json_get_member(buf, result, "unsigned_tx")); - tx = bitcoin_tx_from_hex(fr, hex, strlen(hex)); - tx->chainparams = chainparams; - - /* Find the funding amount */ - funding_found = false; - for (size_t i = 0; i < tx->wtx->num_outputs; i++) { - const u8 *output_script = bitcoin_tx_output_get_script(tmpctx, tx, i); - asset = bitcoin_tx_output_get_amount(tx, i); - - /* We do not support funding a channel with anything but the - * main asset, for now. */ - if (!amount_asset_is_main(&asset)) - continue; - - if (scripteq(output_script, placeholder)) { - funding = amount_asset_to_sat(&asset); - funding_found = true; - break; - } - } - - if (!funding_found) - plugin_err(cmd->plugin, "Error creating placebo funding tx, funding_out not found. %s", hex); - - /* Update funding to actual amount */ - if (fr->funding_all - && !feature_negotiated(plugin_feature_set(cmd->plugin), - fr->their_features, OPT_LARGE_CHANNELS) - && amount_sat_greater(funding, chainparams->max_funding)) - funding = chainparams->max_funding; - - fr->funding_str = type_to_string(fr, struct amount_sat, &funding); - return fundchannel_start(cmd, fr); -} - -static struct command_result *exec_dryrun(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct funding_req *fr) -{ - struct out_req *req; - const jsmntok_t *t; - - /* Stash features so we can wumbo. */ - t = json_get_member(buf, result, "features"); - if (!t) - plugin_err(cmd->plugin, "No features found in connect response?"); - fr->their_features = json_tok_bin_from_hex(fr, buf, t); - if (!fr->their_features) - plugin_err(cmd->plugin, "Bad features '%.*s' in connect response?", - t->end - t->start, buf + t->start); - - req = jsonrpc_request_start(cmd->plugin, cmd, "txprepare", - post_dryrun, forward_error, - fr); - - /* Now that we've tried connecting, we do a 'dry-run' of txprepare, - * so we can get an accurate idea of the funding amount */ - txprepare(req->js, fr, placeholder_funding_addr); - - return send_outreq(cmd->plugin, req); - -} - -static struct command_result *connect_to_peer(struct command *cmd, - struct funding_req *fr) -{ - struct out_req *req = jsonrpc_request_start(cmd->plugin, cmd, "connect", - exec_dryrun, forward_error, - fr); - - json_add_string(req->js, "id", node_id_to_hexstr(tmpctx, fr->id)); - - return send_outreq(cmd->plugin, req); -} - -/* We will use 'id' and 'amount' to build a output: {id: amount}. - * For array type, if we miss 'amount', next parameter will be - * mistaken for 'amount'. - * Note the check for 'output' in 'txprepare' is behind of the checks - * for other parameter, so doing a simply check for 'amount' here can - * help us locate error correctly. - */ -static struct command_result *param_string_check_sat(struct command *cmd, const char *name, - const char * buffer, const jsmntok_t *tok, - const char **str) -{ - struct command_result *res; - struct amount_sat *amount; - - res = param_sat_or_all(cmd, name, buffer, tok, &amount); - if (res) - return res; - - return param_string(cmd, name, buffer, tok, str); -} - -static struct command_result *json_fundchannel(struct command *cmd, - const char *buf, - const jsmntok_t *params) -{ - struct funding_req *fr = tal(cmd, struct funding_req); - - if (!param(cmd, buf, params, - p_req("id", param_node_id, &fr->id), - p_req("amount", param_string_check_sat, &fr->funding_str), - p_opt("feerate", param_string, &fr->feerate_str), - p_opt_def("announce", param_bool, &fr->announce_channel, true), - p_opt_def("minconf", param_number, &fr->minconf, 1), - p_opt("utxos", param_string, &fr->utxo_str), - p_opt("push_msat", param_msat, &fr->push_msat), - NULL)) - return command_param_failed(); - - fr->funding_all = streq(fr->funding_str, "all"); - - return connect_to_peer(cmd, fr); -} - -static void init(struct plugin *p, - const char *buf UNUSED, const jsmntok_t *config UNUSED) -{ - /* Figure out what the 'placeholder' addr is */ - const char *network_name; - u8 *placeholder = tal_hexdata(tmpctx, placeholder_script, strlen(placeholder_script)); - - network_name = rpc_delve(tmpctx, p, "listconfigs", - take(json_out_obj(NULL, "config", - "network")), - ".network"); - chainparams = chainparams_for_network(network_name); - placeholder_funding_addr = encode_scriptpubkey_to_addr(NULL, chainparams, - placeholder); -} - - -static const struct plugin_command commands[] = { { - "fundchannel", - "channels", - "Fund channel with {id} using {amount} (or 'all'), at optional {feerate}. " - "Only use outputs that have {minconf} confirmations.", - "Initiaties a channel open with node 'id'. Must " - "be connected to the node and have enough funds available at the requested minimum confirmation " - "depth (minconf)", - json_fundchannel - } -}; - - -int main(int argc, char *argv[]) -{ - setup_locale(); - plugin_main(argv, init, PLUGIN_RESTARTABLE, true, NULL, commands, - ARRAY_SIZE(commands), NULL, 0, NULL, 0, NULL); -} diff --git a/plugins/spender/fundchannel.c b/plugins/spender/fundchannel.c new file mode 100644 index 000000000000..d369bb61f097 --- /dev/null +++ b/plugins/spender/fundchannel.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include + +static struct command_result * +json_fundchannel(struct command *cmd, + const char *buf, + const jsmntok_t *params); + +const struct plugin_command fundchannel_commands[] = { { + "fundchannel", + "channels", + "Fund channel with {id} using {amount} (or 'all'), at optional {feerate}. " + "Only use outputs that have {minconf} confirmations.", + "Initiaties a channel open with node 'id'. Must " + "be connected to the node and have enough funds available at the requested minimum confirmation " + "depth (minconf)", + json_fundchannel + } +}; +const size_t num_fundchannel_commands = ARRAY_SIZE(fundchannel_commands); + +static struct command_result * +fundchannel_get_result(struct command *cmd, + const char *buf, + const jsmntok_t *result, + void *nothing UNUSED); + +/* Thin wrapper aroud multifundchannel. */ +static struct command_result * +json_fundchannel(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + const char *id; + const jsmntok_t *amount; + const jsmntok_t *feerate; + const jsmntok_t *announce; + const jsmntok_t *minconf; + const jsmntok_t *utxos; + const jsmntok_t *push_msat; + + struct out_req *req; + + if (!param(cmd, buf, params, + p_req("id", param_string, &id), + p_req("amount", param_tok, &amount), + p_opt("feerate", param_tok, &feerate), + p_opt("announce", param_tok, &announce), + p_opt("minconf", param_tok, &minconf), + p_opt("utxos", param_tok, &utxos), + p_opt("push_msat", param_tok, &push_msat), + NULL)) + return command_param_failed(); + + req = jsonrpc_request_start(cmd->plugin, cmd, "multifundchannel", + &fundchannel_get_result, &forward_error, + NULL); + + json_array_start(req->js, "destinations"); + json_object_start(req->js, NULL); + json_add_string(req->js, "id", id); + json_add_tok(req->js, "amount", amount, buf); + if (announce) + json_add_tok(req->js, "announce", announce, buf); + if (push_msat) + json_add_tok(req->js, "push_msat", push_msat, buf); + json_object_end(req->js); + json_array_end(req->js); + if (feerate) + json_add_tok(req->js, "feerate", feerate, buf); + if (minconf) + json_add_tok(req->js, "minconf", minconf, buf); + if (utxos) + json_add_tok(req->js, "utxos", utxos, buf); + + return send_outreq(cmd->plugin, req); +} + +static struct command_result * +fundchannel_get_result(struct command *cmd, + const char *buf, + const jsmntok_t *result, + void *nothing UNUSED) +{ + bool ok; + const jsmntok_t *tx; + const jsmntok_t *txid; + const jsmntok_t *channel_ids_array; + const jsmntok_t *channel_ids_obj; + const jsmntok_t *channel_id; + const jsmntok_t *outnum; + + struct json_stream *out; + + ok = true; + tx = ok ? json_get_member(buf, result, "tx") : NULL; + ok = ok && tx; + txid = ok ? json_get_member(buf, result, "txid") : NULL; + ok = ok && txid; + channel_ids_array = ok ? json_get_member(buf, result, "channel_ids") : NULL; + ok = ok && channel_ids_array; + channel_ids_obj = ok ? json_get_arr(channel_ids_array, 0) : NULL; + ok = ok && channel_ids_obj; + channel_id = ok ? json_get_member(buf, channel_ids_obj, "channel_id") : NULL; + ok = ok && channel_id; + outnum = ok ? json_get_member(buf, channel_ids_obj, "outnum") : NULL; + ok = ok && outnum; + + if (!ok) + plugin_err(cmd->plugin, + "Unexpected result from nultifundchannel: %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + + out = jsonrpc_stream_success(cmd); + json_add_tok(out, "tx", tx, buf); + json_add_tok(out, "txid", txid, buf); + json_add_tok(out, "channel_id", channel_id, buf); + json_add_tok(out, "outnum", outnum, buf); + return command_finished(cmd, out); +} diff --git a/plugins/spender/fundchannel.h b/plugins/spender/fundchannel.h new file mode 100644 index 000000000000..80bd7b5e749a --- /dev/null +++ b/plugins/spender/fundchannel.h @@ -0,0 +1,10 @@ +#ifndef LIGHTNING_PLUGINS_SPENDER_FUNDCHANNEL_H +#define LIGHTNING_PLUGINS_SPENDER_FUNDCHANNEL_H +#include "config.h" + +#include + +extern const struct plugin_command fundchannel_commands[]; +extern const size_t num_fundchannel_commands; + +#endif /* LIGHTNING_PLUGINS_SPENDER_FUNDCHANNEL_H */ diff --git a/plugins/spender/main.c b/plugins/spender/main.c index 9a2861e6bd9a..3d141aa0d2fe 100644 --- a/plugins/spender/main.c +++ b/plugins/spender/main.c @@ -1,6 +1,8 @@ #include #include #include +#include +#include /*~ The spender plugin contains various commands that handle * spending from the onchain wallet. */ @@ -21,6 +23,8 @@ int main(int argc, char **argv) commands = tal_arr(owner, struct plugin_command, 0); tal_expand(&commands, multiwithdraw_commands, num_multiwithdraw_commands); + tal_expand(&commands, fundchannel_commands, num_fundchannel_commands); + tal_expand(&commands, multifundchannel_commands, num_multifundchannel_commands); /* tal_expand(&commands, whatever_commands, num_whatever_commands); */ plugin_main(argv, &spender_init, PLUGIN_STATIC, true, diff --git a/plugins/multifundchannel.c b/plugins/spender/multifundchannel.c similarity index 98% rename from plugins/multifundchannel.c rename to plugins/spender/multifundchannel.c index d1f78a0390ad..20b0520511cd 100644 --- a/plugins/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -2006,7 +2006,6 @@ json_multifundchannel(struct command *cmd, return perform_multifundchannel(mfc); } -static const struct plugin_command multifundchannel_commands[] = { { "multifundchannel", @@ -2022,32 +2021,6 @@ const struct plugin_command multifundchannel_commands[] = { json_multifundchannel } }; -static -const unsigned int num_multifundchannel_commands = +const size_t num_multifundchannel_commands = ARRAY_SIZE(multifundchannel_commands); -static -void multifundchannel_init(struct plugin *plugin, - const char *buf UNUSED, - const jsmntok_t *config UNUSED) -{ - /* Save our chainparams. */ - const char *network_name; - - network_name = rpc_delve(tmpctx, plugin, "listconfigs", - take(json_out_obj(NULL, "config", - "network")), - ".network"); - chainparams = chainparams_for_network(network_name); -} - -int main(int argc, char **argv) -{ - setup_locale(); - plugin_main(argv, - &multifundchannel_init, PLUGIN_RESTARTABLE, - true, - NULL, - multifundchannel_commands, num_multifundchannel_commands, - NULL, 0, NULL, 0, NULL); -} diff --git a/plugins/spender/multifundchannel.h b/plugins/spender/multifundchannel.h new file mode 100644 index 000000000000..abb6c964b1f2 --- /dev/null +++ b/plugins/spender/multifundchannel.h @@ -0,0 +1,10 @@ +#ifndef LIGHTNING_PLUGINS_SPENDER_MULTIFUNDCHANNEL_H +#define LIGHTNING_PLUGINS_SPENDER_MULTIFUNDCHANNEL_H +#include "config.h" + +#include + +extern const struct plugin_command multifundchannel_commands[]; +extern const size_t num_multifundchannel_commands; + +#endif /* LIGHTNING_PLUGINS_SPENDER_MULTIFUNDCHANNEL_H */ diff --git a/tests/test_closing.py b/tests/test_closing.py index 6f84f5ac979f..14dc7e6cd77f 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -2528,11 +2528,13 @@ def test_option_upfront_shutdown_script(node_factory, bitcoind, executor): # Figure out what address it will try to use. keyidx = int(l1.db_query("SELECT intval FROM vars WHERE name='bip32_max_index';")[0]['intval']) - # Expect 1 for change address, 1 for the channel final address, - # which are discarded as the 'scratch' tx that the fundchannel - # plugin makes, plus 1 for the funding address of the actual + # Expect 1 for change address, plus 1 for the funding address of the actual # funding tx. - addr = l1.rpc.call('dev-listaddrs', [keyidx + 3])['addresses'][-1] + addr = l1.rpc.call('dev-listaddrs', [keyidx + 2])['addresses'][-1] + # the above used to be keyidx + 3, but that was when `fundchannel` + # used the `txprepare`-`txdiscard`-`txprepare` trick, which skipped + # one address in the discarded tx. + # Now we use PSBTs, which means we never discard and skip an address. # Now, if we specify upfront and it's OK, all good. l1.stop() diff --git a/tests/test_connection.py b/tests/test_connection.py index 9608a62fd97c..eeed39b302e8 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -784,7 +784,7 @@ def test_funding_fail(node_factory, bitcoind): l1.rpc.connect(l2.info['id'], 'localhost', l2.port) # We don't have enough left to cover fees if we try to spend it all. - with pytest.raises(RpcError, match=r'Could not afford'): + with pytest.raises(RpcError, match=r'not afford'): l1.rpc.fundchannel(l2.info['id'], funds) # Should still be connected.