From 5b6b012af9d0a2b1ca39d1bb190944a4a3111308 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 20 Oct 2020 20:18:19 -0500 Subject: [PATCH] mfc: add happy path-way for v2 in multifundchannel Tested and works with both sides funding! Yay. Doesn't do any amount of reasonable cleanup or handling of errors. --- plugins/Makefile | 8 +- plugins/spender/main.c | 8 +- plugins/spender/multifundchannel.c | 246 +----- plugins/spender/multifundchannel.h | 210 +++++ plugins/spender/openchannel.c | 1148 ++++++++++++++++++++++++++++ plugins/spender/openchannel.h | 16 + 6 files changed, 1425 insertions(+), 211 deletions(-) create mode 100644 plugins/spender/openchannel.c create mode 100644 plugins/spender/openchannel.h diff --git a/plugins/Makefile b/plugins/Makefile index fd7b05c13..94559a79d 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -25,12 +25,14 @@ PLUGIN_SPENDER_SRC := \ plugins/spender/fundchannel.c \ plugins/spender/main.c \ plugins/spender/multifundchannel.c \ - plugins/spender/multiwithdraw.c + plugins/spender/multiwithdraw.c \ + plugins/spender/openchannel.c PLUGIN_SPENDER_HEADER := \ plugins/spender/multifundchannel.h \ plugins/spender/multiwithdraw.h \ plugins/spender/fundchannel.h \ - plugins/spender/multifundchannel.h + plugins/spender/multifundchannel.h \ + plugins/spender/openchannel.h PLUGIN_SPENDER_OBJS := $(PLUGIN_SPENDER_SRC:.c=.o) PLUGIN_ALL_SRC := \ @@ -114,7 +116,7 @@ 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) common/gossmap.o common/route.o common/dijkstra.o $(PLUGIN_KEYSEND_OBJS): $(PLUGIN_PAY_LIB_HEADER) -plugins/spenderp: bitcoin/chainparams.o $(PLUGIN_SPENDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) +plugins/spenderp: bitcoin/chainparams.o bitcoin/psbt.o common/psbt_open.o $(PLUGIN_SPENDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(PLUGIN_ALL_OBJS): $(PLUGIN_LIB_HEADER) diff --git a/plugins/spender/main.c b/plugins/spender/main.c index 881dc22ea..31b3e0842 100644 --- a/plugins/spender/main.c +++ b/plugins/spender/main.c @@ -3,6 +3,7 @@ #include #include #include +#include /*~ The spender plugin contains various commands that handle * spending from the onchain wallet. */ @@ -10,6 +11,7 @@ static void spender_init(struct plugin *p, const char *b, const jsmntok_t *t) { + openchannel_init(p, b, t); /* whatever_init(p, b, t); */ } @@ -17,6 +19,7 @@ int main(int argc, char **argv) { char *owner = tal(NULL, char); struct plugin_command *commands; + struct plugin_notification *notifs; setup_locale(); @@ -27,10 +30,13 @@ int main(int argc, char **argv) tal_expand(&commands, multifundchannel_commands, num_multifundchannel_commands); /* tal_expand(&commands, whatever_commands, num_whatever_commands); */ + notifs = tal_arr(owner, struct plugin_notification, 0); + tal_expand(¬ifs, openchannel_notifs, num_openchannel_notifs); + plugin_main(argv, &spender_init, PLUGIN_STATIC, true, NULL, commands, tal_count(commands), - NULL, 0, + notifs, tal_count(notifs), NULL, 0, NULL); diff --git a/plugins/spender/multifundchannel.c b/plugins/spender/multifundchannel.c index 15fb6e57d..e3bbf7886 100644 --- a/plugins/spender/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -16,201 +16,17 @@ #include #include #include +#include #include +#include #include #include #include +#include #include #include #include -/* Current state of the funding process. */ -enum multifundchannel_state { - /* We have not yet performed `fundchannel_start`. */ - MULTIFUNDCHANNEL_START_NOT_YET = 0, - /* The `connect` command failed. `*/ - MULTIFUNDCHANNEL_CONNECT_FAILED, - /* The `fundchannel_start` command succeeded. */ - MULTIFUNDCHANNEL_STARTED, - /* The `fundchannel_start` command failed. */ - MULTIFUNDCHANNEL_START_FAILED, - /* The `fundchannel_complete` command failed. */ - MULTIFUNDCHANNEL_COMPLETE_FAILED, - /* The transaction might now be broadcasted. */ - MULTIFUNDCHANNEL_DONE -}; - -/* The object for a single destination. */ -struct multifundchannel_destination { - /* The overall multifundchannel command object. */ - struct multifundchannel_command *mfc; - - /* The overall multifundchannel_command contains an - array of multifundchannel_destinations. - This provides the index within the array. - - This is used in debug printing. - */ - unsigned int index; - - /* ID for this destination. */ - struct node_id id; - /* Address hint for this destination, NULL if not - specified. - */ - const char *addrhint; - /* The features this destination has. */ - const u8 *their_features; - - /* Whether we have `fundchannel_start`, failed `connect` or - `fundchannel_complete`, etc. - */ - enum multifundchannel_state state; - - /* The actual target script and address. */ - const u8 *funding_script; - const char *funding_addr; - - /* The bitcoin address to close to */ - const char *close_to_str; - - /* The scriptpubkey we will close to. Only set if - * peer supports opt_upfront_shutdownscript and - * we passsed in a valid close_to_str */ - const u8 *close_to_script; - - /* The amount to be funded for this destination. - If the specified amount is "all" then the `all` - flag is set, and the amount is initially 0 until - we have figured out how much exactly "all" is, - after the dryrun stage. - */ - bool all; - struct amount_sat amount; - - /* The output index for this destination. */ - unsigned int outnum; - - /* Whether the channel to this destination will - be announced. - */ - bool announce; - /* How much of the initial funding to push to - the destination. - */ - struct amount_msat push_msat; - - /* The actual channel_id. */ - const char *channel_id; - - /* Any error messages. */ - const char *error; - errcode_t code; -}; - -/* Stores a destination that was removed due to some failure. */ -struct multifundchannel_removed { - /* The destination we removed. */ - struct node_id id; - /* The method that failed: - connect, fundchannel_start, fundchannel_complete. - */ - const char *method; - /* The error that caused this destination to be removed, in JSON. */ - const char *error; - errcode_t code; -}; - -/* The object for a single multifundchannel command. */ -struct multifundchannel_command { - /* A unique numeric identifier for this particular - multifundchannel execution. - - This is used for debug logs; we want to be able to - identify *which* multifundchannel is being described - in the debug logs, especially if the user runs - multiple `multifundchannel` commands in parallel, or - in very close sequence, which might confuse us with - *which* debug message belongs with *which* command. - - We actually just reuse the id from the cmd. - Store it here for easier access. - */ - u64 id; - - /* The plugin-level command. */ - struct command *cmd; - /* An array of destinations. */ - struct multifundchannel_destination *destinations; - /* Number of pending parallel fundchannel_start or - fundchannel_complete. - */ - size_t pending; - - /* The feerate desired by the user. - * If cmtmt_feerate_str is present, will only be used - * for the funding transaction. */ - const char *feerate_str; - - /* The feerate desired by the user for - * the channel commitment and HTLC txs. - * If not provided, defaults to the feerate_str - * value. */ - const char *cmtmt_feerate_str; - - /* The minimum number of confirmations for owned - UTXOs to be selected. - */ - u32 minconf; - /* The set of utxos to be used. */ - const char *utxos_str; - /* How long should we keep going if things fail. */ - size_t minchannels; - /* Array of destinations that were removed in a best-effort - attempt to fund as many channels as possible. - */ - struct multifundchannel_removed *removeds; - - /* The PSBT of the funding transaction we are building. - Prior to `fundchannel_start` completing for all destinations, - this contains an unsigned incomplete transaction that is just a - reservation of the inputs. - After `fundchannel_start`, this contains an unsigned transaction - with complete outputs. - After `fundchannel_complete`, this contains a signed, finalized - transaction. - */ - struct wally_psbt *psbt; - /* The actual feerate of the PSBT. */ - u32 feerate_per_kw; - /* The expected weight of the PSBT after adding in all the outputs. - * In weight units (sipa). */ - u32 estimated_final_weight; - /* Excess satoshi from the PSBT. - * If "all" this is the entire amount; if not "all" this is the - * proposed change amount, which if dusty should be donated to - * the miners. - */ - struct amount_sat excess_sat; - - /* A convenient change address. NULL at the start, filled in - * if we detect we need it. */ - const u8 *change_scriptpubkey; - /* Whether we need a change output. */ - bool change_needed; - /* The change amount. */ - struct amount_sat change_amount; - - /* The txid of the final funding transaction. */ - struct bitcoin_txid *txid; - - /* The actual tx of the actual final funding transaction - that was broadcast. - */ - const char *final_tx; - const char *final_txid; -}; - extern const struct chainparams *chainparams; /* Flag set when any of the destinations has a value of "all". */ @@ -367,14 +183,6 @@ mfc_cleanup_complete(struct multifundchannel_cleanup *cleanup) static struct command_result * mfc_fail(struct multifundchannel_command *, errcode_t code, const char *fmt, ...); -/* Use this instead of forward_error. */ -static struct command_result * -mfc_forward_error(struct command *cmd, - const char *buf, const jsmntok_t *error, - struct multifundchannel_command *); -/* Use this instead of command_finished. */ -static struct command_result * -mfc_finished(struct multifundchannel_command *, struct json_stream *response); /* Use this instead of command_err_raw. */ static struct command_result * mfc_err_raw(struct multifundchannel_command *, const char *json_string); @@ -443,7 +251,7 @@ mfc_err_raw_complete(struct mfc_err_raw_object *obj) "mfc %"PRIu64": cleanup done, failing raw.", obj->mfc->id); return command_err_raw(obj->mfc->cmd, obj->error); } -static struct command_result * +struct command_result * mfc_forward_error(struct command *cmd, const char *buf, const jsmntok_t *error, struct multifundchannel_command *mfc) @@ -461,7 +269,7 @@ struct mfc_finished_object { }; static struct command_result * mfc_finished_complete(struct mfc_finished_object *obj); -static struct command_result * +struct command_result * mfc_finished(struct multifundchannel_command *mfc, struct json_stream *response) { @@ -569,8 +377,9 @@ param_destinations_array(struct command *cmd, const char *name, dest->amount = dest->all ? AMOUNT_SAT(0) : *amount; dest->announce = *announce; dest->push_msat = *push_msat; - dest->channel_id = NULL; dest->error = NULL; + dest->psbt = NULL; + dest->updated_psbt = NULL; /* Only one destination can have "all" indicator. */ if (dest->all) { @@ -1086,11 +895,15 @@ after_newaddr(struct command *cmd, } static struct command_result * -perform_fundchannel_start(struct multifundchannel_command *mfc); +perform_channel_start(struct multifundchannel_command *mfc); static struct command_result * mfc_psbt_acquired(struct multifundchannel_command *mfc) { - return perform_fundchannel_start(mfc); + /* Add serials to all of our input/outputs, so they're stable + * for the life of the tx */ + psbt_add_serials(mfc->psbt, TX_INITIATOR); + + return perform_channel_start(mfc); } /*---------------------------------------------------------------------------*/ @@ -1116,17 +929,30 @@ steps if we take too long before running static void fundchannel_start_dest(struct multifundchannel_destination *dest); static struct command_result * -perform_fundchannel_start(struct multifundchannel_command *mfc) +perform_channel_start(struct multifundchannel_command *mfc) { unsigned int i; plugin_log(mfc->cmd->plugin, LOG_DBG, - "mfc %"PRIu64": fundchannel_start parallel.", mfc->id); + "mfc %"PRIu64": fundchannel_start parallel " + "with PSBT %s", + mfc->id, + type_to_string(tmpctx, struct wally_psbt, mfc->psbt)); mfc->pending = tal_count(mfc->destinations); - for (i = 0; i < tal_count(mfc->destinations); ++i) - fundchannel_start_dest(&mfc->destinations[i]); + /* Since v2 is now available, we branch depending + * on the capability of the peer and our feaures */ + for (i = 0; i < tal_count(mfc->destinations); ++i) { +#if EXPERIMENTAL_FEATURES + if (feature_negotiated(plugin_feature_set(mfc->cmd->plugin), + mfc->destinations[i].their_features, + OPT_DUAL_FUND)) { + openchannel_init_dest(&mfc->destinations[i]); + } else +#endif /* EXPERIMENTAL_FEATURES */ + fundchannel_start_dest(&mfc->destinations[i]); + } assert(mfc->pending != 0); return command_still_pending(mfc->cmd); @@ -1240,6 +1066,7 @@ fundchannel_start_ok(struct command *cmd, return fundchannel_start_done(dest); } + static struct command_result * fundchannel_start_err(struct command *cmd, const char *buf, @@ -1546,7 +1373,7 @@ fundchannel_complete_ok(struct command *cmd, "fundchannel_complete no channel_id: %.*s", json_tok_full_len(result), json_tok_full(buf, result)); - dest->channel_id = json_strdup(mfc, buf, channel_id_tok); + json_to_channel_id(buf, channel_id_tok, &dest->channel_id); return fundchannel_complete_done(dest); } @@ -1803,7 +1630,8 @@ multifundchannel_finished(struct multifundchannel_command *mfc) for (i = 0; i < tal_count(mfc->destinations); ++i) { json_object_start(out, NULL); json_add_node_id(out, "id", &mfc->destinations[i].id); - json_add_string(out, "channel_id", mfc->destinations[i].channel_id); + json_add_channel_id(out, "channel_id", + &mfc->destinations[i].channel_id); json_add_num(out, "outnum", mfc->destinations[i].outnum); if (mfc->destinations[i].close_to_script) json_add_hex_talarr(out, "close_to", @@ -1869,6 +1697,11 @@ static bool dest_failed(struct multifundchannel_destination *dest) case MULTIFUNDCHANNEL_START_FAILED: case MULTIFUNDCHANNEL_COMPLETE_FAILED: return true; + case MULTIFUNDCHANNEL_FAILED: + case MULTIFUNDCHANNEL_SECURED: + case MULTIFUNDCHANNEL_UPDATED: + case MULTIFUNDCHANNEL_SIGNED: + abort(); // FIXME, for openchannel } abort(); } @@ -2060,4 +1893,3 @@ const struct plugin_command multifundchannel_commands[] = { }; const size_t num_multifundchannel_commands = ARRAY_SIZE(multifundchannel_commands); - diff --git a/plugins/spender/multifundchannel.h b/plugins/spender/multifundchannel.h index abb6c964b..feb6a108c 100644 --- a/plugins/spender/multifundchannel.h +++ b/plugins/spender/multifundchannel.h @@ -2,9 +2,219 @@ #define LIGHTNING_PLUGINS_SPENDER_MULTIFUNDCHANNEL_H #include "config.h" +#include +#include #include extern const struct plugin_command multifundchannel_commands[]; extern const size_t num_multifundchannel_commands; +/* Current state of the funding process. */ +enum multifundchannel_state { + /* We have not yet performed `fundchannel_start`. */ + MULTIFUNDCHANNEL_START_NOT_YET = 0, + /* The `connect` command failed. `*/ + MULTIFUNDCHANNEL_CONNECT_FAILED, + /* The `fundchannel_start` command succeeded. */ + MULTIFUNDCHANNEL_STARTED, + /* The `fundchannel_start` command failed. */ + MULTIFUNDCHANNEL_START_FAILED, + /* The `fundchannel_complete` command failed. */ + MULTIFUNDCHANNEL_COMPLETE_FAILED, + /* The transaction might now be broadcasted. */ + MULTIFUNDCHANNEL_DONE, + + /* FIXME: clean up for interleaved handling */ + MULTIFUNDCHANNEL_UPDATED, + MULTIFUNDCHANNEL_SECURED, + MULTIFUNDCHANNEL_SIGNED, + MULTIFUNDCHANNEL_FAILED, +}; + +/* Stores a destination that was removed due to some failure. */ +struct multifundchannel_removed { + /* The destination we removed. */ + struct node_id id; + /* The method that failed: + connect, fundchannel_start, fundchannel_complete. + */ + const char *method; + /* The error that caused this destination to be removed, in JSON. */ + const char *error; + errcode_t code; +}; + +/* the object for a single destination. */ +struct multifundchannel_destination { + /* the overall multifundchannel command object. */ + struct multifundchannel_command *mfc; + + /* the overall multifundchannel_command contains an + array of multifundchannel_destinations. + this provides the index within the array. + + this is used in debug printing. + */ + unsigned int index; + + /* id for this destination. */ + struct node_id id; + /* address hint for this destination, null if not + specified. + */ + const char *addrhint; + /* the features this destination has. */ + const u8 *their_features; + + /* whether we have `fundchannel_start`, failed `connect` or + `fundchannel_complete`, etc. + */ + enum multifundchannel_state state; + + /* the actual target script and address. */ + const u8 *funding_script; + const char *funding_addr; + + /* the upfront shutdown script for this channel */ + const char *close_to_str; + + /* The scriptpubkey we will close to. Only set if + * peer supports opt_upfront_shutdownscript and + * we passsed in a valid close_to_str */ + const u8 *close_to_script; + + /* the amount to be funded for this destination. + if the specified amount is "all" then the `all` + flag is set, and the amount is initially 0 until + we have figured out how much exactly "all" is, + after the dryrun stage. + */ + bool all; + struct amount_sat amount; + + /* the output index for this destination. */ + unsigned int outnum; + + /* whether the channel to this destination will + be announced. + */ + bool announce; + /* how much of the initial funding to push to + the destination. + */ + struct amount_msat push_msat; + + /* the actual channel_id. */ + struct channel_id channel_id; + + /* any error messages. */ + const char *error; + errcode_t code; + + /* yarr v2, ahoy! */ + struct wally_psbt *psbt; + struct wally_psbt *updated_psbt; + u64 funding_serial; +}; + + +/* The object for a single multifundchannel command. */ +struct multifundchannel_command { + /* A unique numeric identifier for this particular + multifundchannel execution. + + This is used for debug logs; we want to be able to + identify *which* multifundchannel is being described + in the debug logs, especially if the user runs + multiple `multifundchannel` commands in parallel, or + in very close sequence, which might confuse us with + *which* debug message belongs with *which* command. + + We actually just reuse the id from the cmd. + Store it here for easier access. + */ + u64 id; + + /* The plugin-level command. */ + struct command *cmd; + /* An array of destinations. */ + struct multifundchannel_destination *destinations; + /* Number of pending parallel fundchannel_start or + fundchannel_complete. + */ + size_t pending; + + /* The feerate desired by the user. */ + const char *feerate_str; + + /* If specified, the feerate to be used for channel commitment + * transactions. Defaults to the `feerate_str` if not provided. */ + const char *cmtmt_feerate_str; + + /* The minimum number of confirmations for owned + UTXOs to be selected. + */ + u32 minconf; + /* The set of utxos to be used. */ + const char *utxos_str; + /* How long should we keep going if things fail. */ + size_t minchannels; + /* Array of destinations that were removed in a best-effort + attempt to fund as many channels as possible. + */ + struct multifundchannel_removed *removeds; + + /* The PSBT of the funding transaction we are building. + Prior to `fundchannel_start` completing for all destinations, + this contains an unsigned incomplete transaction that is just a + reservation of the inputs. + After `fundchannel_start`, this contains an unsigned transaction + with complete outputs. + After `fundchannel_complete`, this contains a signed, finalized + transaction. + */ + struct wally_psbt *psbt; + /* The actual feerate of the PSBT. */ + u32 feerate_per_kw; + /* The expected weight of the PSBT after adding in all the outputs. + * In weight units (sipa). */ + u32 estimated_final_weight; + /* Excess satoshi from the PSBT. + * If "all" this is the entire amount; if not "all" this is the + * proposed change amount, which if dusty should be donated to + * the miners. + */ + struct amount_sat excess_sat; + + /* A convenient change address. NULL at the start, filled in + * if we detect we need it. */ + const u8 *change_scriptpubkey; + /* Whether we need a change output. */ + bool change_needed; + /* The change amount. */ + struct amount_sat change_amount; + + /* The txid of the final funding transaction. */ + struct bitcoin_txid *txid; + + /* The actual tx of the actual final funding transaction + that was broadcast. + */ + const char *final_tx; + const char *final_txid; + + /* V2 things */ + struct list_node list; +}; + +/* Use this instead of forward_error. */ +struct command_result * +mfc_forward_error(struct command *cmd, + const char *buf, const jsmntok_t *error, + struct multifundchannel_command *); + +/* Use this instead of command_finished. */ +struct command_result * +mfc_finished(struct multifundchannel_command *, struct json_stream *response); + #endif /* LIGHTNING_PLUGINS_SPENDER_MULTIFUNDCHANNEL_H */ diff --git a/plugins/spender/openchannel.c b/plugins/spender/openchannel.c new file mode 100644 index 000000000..3e1622fa0 --- /dev/null +++ b/plugins/spender/openchannel.c @@ -0,0 +1,1148 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct list_head mfc_commands; + +static void +destroy_mfc(struct multifundchannel_command *mfc) +{ + list_del(&mfc->list); +} + +static void register_mfc(struct multifundchannel_command *mfc) +{ + assert(mfc); + + list_add_tail(&mfc_commands, &mfc->list); + tal_add_destructor(mfc, &destroy_mfc); +} + +static struct multifundchannel_destination * +find_dest_by_channel_id(struct channel_id *cid) +{ + struct multifundchannel_command *mfc, *n; + + list_for_each_safe (&mfc_commands, mfc, n, list) { + for (size_t i = 0; i < tal_count(mfc->destinations); i++) { + struct multifundchannel_destination *dest = + &mfc->destinations[i]; + if (channel_id_eq(&dest->channel_id, cid)) + return dest; + } + } + + return NULL; +} + +static struct command_result * +redo_multiopenchannel(struct multifundchannel_command *mfc, + const char *failing_method) +{ + // FIXME + plugin_err(mfc->cmd->plugin, + "REDO CALLED AT %s", failing_method); + return NULL; +} + +/* There's a few ground rules here about how we store/keep + * the PSBT input/outputs in such a way that we can Do The + * Right Thing for each of our peers. + * + * c-lightning will make sure that our peer isn't removing/adding + * any updates that it's not allowed to (i.e. ours or a different + * node's that we're pretending are 'ours'). + * + * The parent copy of the PSBT has all of the inputs/outputs added to it, + * but the serial_ids are decremented by one to the even-pair, e.g. a + * serial_id of 3 -> 2; 17 -> 16; etc. + * + * If the even-pair of a provided serial_id is taken/occupied, + * the update is rejected (the parent is unmodified and the + * function returns false). + * + * The peer's inputs/outputs updates are then copied to the parent psbt. + */ +static bool update_parent_psbt(const tal_t *ctx, + struct multifundchannel_destination *dest, + struct wally_psbt *old_node_psbt, + struct wally_psbt *new_node_psbt, + struct wally_psbt **parent_psbt) +{ + struct psbt_changeset *changes; + struct wally_psbt *clone, *new_node_copy; + + /* Clone the parent, so we don't make any changes to it + * until we've succesfully done everything */ + + /* Only failure is alloc, should we even check? */ + tal_wally_start(); + if (wally_psbt_clone_alloc(*parent_psbt, 0, &clone) != WALLY_OK) + abort(); + tal_wally_end(tal_steal(ctx, clone)); + + /* This makes it such that we can reparent/steal added + * inputs/outputs without impacting the 'original'. We + * could avoid this if there was a 'wally_psbt_input_clone_into' + * function, or the like */ + tal_wally_start(); + if (wally_psbt_clone_alloc(new_node_psbt, 0, &new_node_copy) + != WALLY_OK) + abort(); + /* copy is cleaned up below */ + tal_wally_end(NULL); + + changes = psbt_get_changeset(NULL, old_node_psbt, + new_node_copy); + /* Inputs */ + for (size_t i = 0; i < tal_count(changes->added_ins); i++) { + u64 serial; + int s_idx; + const struct wally_psbt_input *in = + &changes->added_ins[i].input; + size_t idx = clone->num_inputs; + + if (!psbt_get_serial_id(&in->unknowns, &serial)) + goto fail; + + /* Ignore any input that's ours */ + if (serial % 2 == TX_INITIATOR) + continue; + + /* Check that serial does not exist on parent already */ + s_idx = psbt_find_serial_input(clone, serial - 1); + if (s_idx != -1) + goto fail; + + psbt_add_input(clone, + &changes->added_ins[i].tx_input, + idx); + /* Move the input over */ + clone->inputs[idx] = *in; + + /* Update the added serial on the clone to the correct + * position */ + psbt_input_set_serial_id(clone, &clone->inputs[idx], + serial - 1); + } + + for (size_t i = 0; i < tal_count(changes->rm_ins); i++) { + u64 serial; + int s_idx; + const struct wally_psbt_input *in = + &changes->rm_ins[i].input; + + if (!psbt_get_serial_id(&in->unknowns, &serial)) + goto fail; + + /* If it's ours, that's a whoops */ + if (serial % 2 == TX_INITIATOR) + goto fail; + + /* Check that serial exists on parent already */ + s_idx = psbt_find_serial_input(clone, serial - 1); + if (s_idx == -1) + goto fail; + + /* Remove input */ + if (wally_psbt_remove_input(clone, s_idx) != WALLY_OK) + goto fail; + } + + /* Outputs */ + for (size_t i = 0; i < tal_count(changes->added_outs); i++) { + u64 serial, parent_serial; + const struct wally_psbt_output *out = + &changes->added_outs[i].output; + int s_idx; + size_t idx = clone->num_outputs; + + if (!psbt_get_serial_id(&out->unknowns, &serial)) + goto fail; + + if (serial % 2 == TX_INITIATOR) { + /* If it's the funding output, we add it */ + if (serial == dest->funding_serial) { + parent_serial = dest->funding_serial; + } else + continue; + } else + parent_serial = serial - 1; + + /* Check that serial does not exist on parent already */ + s_idx = psbt_find_serial_output(clone, parent_serial); + if (s_idx != -1) + goto fail; + + psbt_add_output(clone, + &changes->added_outs[i].tx_output, + idx); + /* Move output over */ + clone->outputs[idx] = *out; + + /* Update the added serial on the clone to the correct + * position */ + psbt_output_set_serial_id(clone, &clone->outputs[idx], + parent_serial); + } + + for (size_t i = 0; i < tal_count(changes->rm_outs); i++) { + u64 serial; + int s_idx; + const struct wally_psbt_output *out = + &changes->rm_outs[i].output; + + if (!psbt_get_serial_id(&out->unknowns, &serial)) + goto fail; + + /* If it's ours, that's a whoops */ + if (serial % 2 == TX_INITIATOR) + goto fail; + + /* Check that serial exists on parent already */ + s_idx = psbt_find_serial_output(clone, serial - 1); + if (s_idx == -1) + goto fail; + + /* Remove output */ + if (wally_psbt_remove_output(clone, s_idx) != WALLY_OK) + goto fail; + } + + /* We want to preserve the memory bits associated with + * the inputs/outputs we just copied over when we free + * the copy, so remove ones the *added* from the copy. + * We go from the back since this will modify the indexes */ + for (size_t i = tal_count(changes->added_ins) - 1; + i > -1; + i--) { + psbt_rm_input(new_node_copy, + changes->added_ins[i].idx); + } + for (size_t i = tal_count(changes->added_outs) - 1; + i > -1; + i--) { + psbt_rm_output(new_node_copy, + changes->added_outs[i].idx); + } + + tal_free(changes); + tal_free(new_node_copy); + + tal_free(*parent_psbt); + *parent_psbt = clone; + return true; + +fail: + tal_free(changes); + tal_free(new_node_copy); + tal_free(clone); + return false; +} + +/* After all of the changes have been applied to the parent psbt, + * we update each node_psbt with the changes from every *other* peer. + * + * This updated node_psbt is the one that we should pass to + * openchannel_update for the next round. + * + * We do update rounds until every peer returns "commitment_secured:true", + * which will happen at the same round (as it requires us passing in + * an identical PSBT as the previous round). + */ +static bool update_node_psbt(const tal_t *ctx, + const struct wally_psbt *parent_psbt, + struct wally_psbt **node_psbt) +{ + /* How to update this? We could do a comparison. + * More easily, we simply clone the parent and update + * the correct serial_ids for the node_psbt */ + struct wally_psbt *clone; + + tal_wally_start(); + /* Only failure is alloc */ + if (wally_psbt_clone_alloc(parent_psbt, 0, &clone) != WALLY_OK) + abort(); + tal_wally_end(tal_steal(ctx, clone)); + + /* For every peer's input/output, flip the serial id + * on the clone. They should all be present. */ + for (size_t i = 0; i < (*node_psbt)->num_inputs; i++) { + u64 serial_id; + int input_index; + if (!psbt_get_serial_id(&(*node_psbt)->inputs[i].unknowns, + &serial_id)) { + tal_wally_end(tal_free(clone)); + return false; + } + + /* We're the initiator here. If it's not the peer's + * input, skip it */ + if (serial_id % 2 == TX_INITIATOR) + continue; + /* Down one, as that's where it'll be on the parent */ + input_index = psbt_find_serial_input(clone, serial_id - 1); + /* Must exist */ + assert(input_index != -1); + /* Update the cloned input serial to match the node's + * view */ + psbt_input_set_serial_id(clone, &clone->inputs[input_index], + serial_id); + + } + + for (size_t i = 0; i < (*node_psbt)->num_outputs; i++) { + u64 serial_id; + int output_index; + if (!psbt_get_serial_id(&(*node_psbt)->outputs[i].unknowns, + &serial_id)) { + tal_wally_end(tal_free(clone)); + return false; + } + /* We're the initiator here. If it's not the peer's + * output, skip it */ + if (serial_id % 2 == TX_INITIATOR) + continue; + + /* Down one, as that's where it'll be on the parent */ + output_index = psbt_find_serial_output(clone, + serial_id - 1); + /* Must exist */ + assert(output_index != -1); + + /* Update the cloned input serial to match the node's + * view */ + psbt_output_set_serial_id(clone, &clone->outputs[output_index], + serial_id); + + } + + tal_free(*node_psbt); + *node_psbt = clone; + return true; +} + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* + * To collect signatures from our peers, we use a notification watcher. + *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* + */ +static struct command_result * +openchannel_finished(struct multifundchannel_command *mfc) +{ + struct json_stream *out; + + plugin_log(mfc->cmd->plugin, LOG_DBG, + "mfc %"PRIu64": done.", mfc->id); + + out = jsonrpc_stream_success(mfc->cmd); + json_add_string(out, "tx", mfc->final_tx); + json_add_string(out, "txid", mfc->final_txid); + json_array_start(out, "channel_ids"); + for (size_t i = 0; i < tal_count(mfc->destinations); i++) { + struct multifundchannel_destination *dest = + &mfc->destinations[i]; + + json_object_start(out, NULL); + json_add_node_id(out, "id", &dest->id); + json_add_channel_id(out, "channel_id", &dest->channel_id); + json_add_num(out, "outnum", dest->outnum); + json_object_end(out); + } + json_array_end(out); + + json_array_start(out, "failed"); + for (size_t i = 0; i < tal_count(mfc->removeds); i++) { + struct multifundchannel_removed *rm = + &mfc->removeds[i]; + json_object_start(out, NULL); + json_add_node_id(out, "id", &rm->id); + json_add_string(out, "method", rm->method); + json_add_jsonstr(out, "error", rm->error); + json_object_end(out); + } + json_array_end(out); + + mfc->psbt = tal_free(mfc->psbt); + return mfc_finished(mfc, out); +} + +static struct command_result * +after_openchannel_signed(struct multifundchannel_command *mfc) +{ + --mfc->pending; + if (mfc->pending == 0) + return openchannel_finished(mfc); + else + return command_still_pending(mfc->cmd); +} + +static struct command_result * +openchannel_signed_ok(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct multifundchannel_destination *dest) +{ + struct multifundchannel_command *mfc = dest->mfc; + + plugin_log(mfc->cmd->plugin, LOG_DBG, + "mfc %"PRIu64", dest %u: `openchannel_signed` done", + mfc->id, dest->index); + + /* One of the other commands might have landed here first */ + if (!mfc->final_tx) { + const jsmntok_t *tx_tok, *txid_tok; + + tx_tok = json_get_member(buf, result, "tx"); + if (!tx_tok) + plugin_err(mfc->cmd->plugin, + "`openchannel_signed` has no 'tx': %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + + mfc->final_tx = json_strdup(mfc, buf, tx_tok); + + txid_tok = json_get_member(buf, result, "txid"); + if (!txid_tok) + plugin_err(mfc->cmd->plugin, + "`openchannel_signed` has no 'txid': %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + + mfc->final_txid = json_strdup(mfc, buf, txid_tok); + } + + /* We done !? */ + dest->psbt = tal_free(dest->psbt); + return after_openchannel_signed(mfc); +} + +static struct command_result * +openchannel_signed_err(struct command *cmd, + const char *buf, + const jsmntok_t *error, + struct multifundchannel_destination *dest) +{ + struct multifundchannel_command *mfc = dest->mfc; + const jsmntok_t *code_tok; + + plugin_log(mfc->cmd->plugin, LOG_DBG, + "mfc %"PRIu64", dest %u: " + "failed! openchannel_signed %s: %.*s.", + mfc->id, dest->index, + node_id_to_hexstr(tmpctx, &dest->id), + json_tok_full_len(error), + json_tok_full(buf, error)); + + code_tok = json_get_member(buf, error, "code"); + if (!code_tok) + plugin_err(cmd->plugin, + "`openchannel_signed` failure did not have `code`? " + "%.*s", + json_tok_full_len(error), + json_tok_full(buf, error)); + if (!json_to_errcode(buf, code_tok, &dest->code)) + plugin_err(cmd->plugin, + "`openchannel_signed` has unparseable `code`? " + "%.*s", + json_tok_full_len(code_tok), + json_tok_full(buf, code_tok)); + + dest->state = MULTIFUNDCHANNEL_FAILED; + dest->error = json_strdup(dest->mfc, buf, error); + return after_openchannel_signed(mfc); +} + +static void +openchannel_signed_dest(struct multifundchannel_destination *dest) +{ + struct multifundchannel_command *mfc = dest->mfc; + struct command *cmd = mfc->cmd; + struct out_req *req; + + plugin_log(cmd->plugin, LOG_DBG, + "mfc %"PRIu64", dest %u: `openchannel_signed` %s " + "psbt %s", + mfc->id, dest->index, + type_to_string(tmpctx, struct channel_id, &dest->channel_id), + type_to_string(tmpctx, struct wally_psbt, dest->psbt)); + + req = jsonrpc_request_start(cmd->plugin, + cmd, + "openchannel_signed", + &openchannel_signed_ok, + &openchannel_signed_err, + dest); + json_add_channel_id(req->js, "channel_id", &dest->channel_id); + json_add_psbt(req->js, "signed_psbt", dest->psbt); + + send_outreq(cmd->plugin, req); +} + +static struct command_result * +perform_openchannel_signed(struct multifundchannel_command *mfc) +{ + plugin_log(mfc->cmd->plugin, LOG_DBG, + "mfc %"PRIu64": parallel `openchannel_signed`.", + mfc->id); + + /* Check that every dest is in the right state */ + for (size_t i = 0; i < tal_count(mfc->destinations); i++) { + struct multifundchannel_destination *dest; + dest = &mfc->destinations[i]; + if (dest->state != MULTIFUNDCHANNEL_SIGNED) { + // FIXME: these channels are all borked. + redo_multiopenchannel(mfc, "openchannel_signed"); + } + } + + if (!psbt_finalize(mfc->psbt)) + plugin_err(mfc->cmd->plugin, + "mfc %"PRIu64": Unable to finalize parent PSBT " + "%s", + type_to_string(tmpctx, struct wally_psbt, + mfc->psbt)); + + mfc->pending = tal_count(mfc->destinations); + for (size_t i = 0; i < tal_count(mfc->destinations); i++) { + /* We need to 'port' all of the sigs down to the + * destination PSBTs */ + update_node_psbt(mfc, mfc->psbt, + &mfc->destinations[i].psbt); + openchannel_signed_dest(&mfc->destinations[i]); + } + + assert(mfc->pending != 0); + return command_still_pending(mfc->cmd); +} + +static struct command_result * +after_psbt_signed(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct multifundchannel_command *mfc) +{ + const jsmntok_t *field; + struct wally_psbt *signed_psbt; + + plugin_log(mfc->cmd->plugin, LOG_DBG, + "mfc %"PRIu64": `signpsbt` completed", + mfc->id); + + field = json_get_member(buf, result, "signed_psbt"); + if (!field) + plugin_err(mfc->cmd->plugin, + "`signpsbt` did not return 'signed_psbt'? %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + if (!json_to_psbt(tmpctx, buf, field, &signed_psbt)) + plugin_err(mfc->cmd->plugin, + "`signpsbt` returned invalid 'signed_psbt' %.*s", + json_tok_full_len(field), + json_tok_full(buf, field)); + + tal_free(mfc->psbt); + mfc->psbt = tal_steal(mfc, signed_psbt); + + return perform_openchannel_signed(mfc); +} + +static struct command_result * +collect_sigs(struct multifundchannel_command *mfc) +{ + /* We need to sign the PSBT, we also need to + * wait for all of the sigs to come in */ + struct out_req *req; + struct bitcoin_txid mfc_txid; + psbt_txid(NULL, mfc->psbt, &mfc_txid, NULL); + + + /* But first! we sanity check that everyone's + * expecting the same funding txid */ + for (size_t i = 0; i < tal_count(mfc->destinations); i++) { + struct multifundchannel_destination *dest; + struct bitcoin_txid dest_txid; + dest = &mfc->destinations[i]; + + assert(dest->state == MULTIFUNDCHANNEL_SECURED || + dest->state == MULTIFUNDCHANNEL_SIGNED); + psbt_txid(NULL, dest->psbt, &dest_txid, NULL); + + assert(bitcoin_txid_eq(&mfc_txid, &dest_txid)); + } + + /* Now we sign our inputs. You do remember which inputs + * are yours, right? */ + plugin_log(mfc->cmd->plugin, LOG_DBG, + "mfc %"PRIu64": signpsbt.", mfc->id); + + req = jsonrpc_request_start(mfc->cmd->plugin, mfc->cmd, + "signpsbt", + &after_psbt_signed, + &mfc_forward_error, + mfc); + json_add_psbt(req->js, "psbt", mfc->psbt); + return send_outreq(mfc->cmd->plugin, req); +} + +static void +openchannel_dest_signed(struct multifundchannel_command *mfc) +{ + bool ready = true; + for (size_t i = 0; i < tal_count(mfc->destinations); i++) + ready &= mfc->destinations[i].state == + MULTIFUNDCHANNEL_SIGNED; + + if (ready) + collect_sigs(mfc); +} + +static void json_peer_sigs(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + const jsmntok_t *cid_tok, *psbt_tok; + struct channel_id cid; + const struct wally_psbt *psbt; + struct multifundchannel_destination *dest; + + cid_tok = json_delve(buf, params, ".openchannel_peer_sigs.channel_id"); + if (!cid_tok) + plugin_err(cmd->plugin, + "`openchannel_peer_sigs` notification did not " + "send 'channel_id'? %.*s", + json_tok_full_len(params), + json_tok_full(buf, params)); + if (!json_to_channel_id(buf, cid_tok, &cid)) + plugin_err(cmd->plugin, + "Unable to parse openchannel_peer_sigs.channel_id " + "%.*s", + json_tok_full_len(params), + json_tok_full(buf, params)); + + psbt_tok= json_delve(buf, params, ".openchannel_peer_sigs.signed_psbt"); + if (!psbt_tok) + plugin_err(cmd->plugin, + "`openchannel_peer_sigs` notification did not " + "include 'signed_psbt'? %.*s", + json_tok_full_len(params), + json_tok_full(buf, params)); + + if (!json_to_psbt(cmd, buf, psbt_tok, + cast_const2(struct wally_psbt **, &psbt))) + plugin_err(cmd->plugin, + "Unable to parse openchannel_peer_sigs.signed_psbt " + "%.*s", + json_tok_full_len(params), + json_tok_full(buf, params)); + + /* Find the destination that's got this channel_id on it! */ + dest = find_dest_by_channel_id(&cid); + if (!dest) { + /* if there's no pending destination... whatever */ + plugin_log(cmd->plugin, LOG_DBG, + "mfc ??: `openchannel_peer_sigs` no " + "pending dest found for channel_id %s", + type_to_string(tmpctx, struct channel_id, &cid)); + return; + } + + plugin_log(cmd->plugin, LOG_DBG, + "mfc %"PRIu64":`openchannel_peer_sigs` notice received for" + " channel %s", + dest->mfc->id, + tal_hexstr(tmpctx, &cid, sizeof(cid))); + + assert(dest->state == MULTIFUNDCHANNEL_SECURED); + + /* Combine with the parent. Unknown map dupes are ignored, + * so the updated serial_id should persist on the parent */ + tal_wally_start(); + if (wally_psbt_combine(dest->mfc->psbt, psbt) != WALLY_OK) + plugin_err(cmd->plugin, + "mfc %"PRIu64": Unable to combine signed " + "PSBT with roll-up. " + "Signed %s, prev %s", dest->mfc->id, + type_to_string(tmpctx, struct wally_psbt, psbt), + type_to_string(tmpctx, struct wally_psbt, + dest->mfc->psbt)); + + tal_wally_end(dest->mfc->psbt); + dest->state = MULTIFUNDCHANNEL_SIGNED; + openchannel_dest_signed(dest->mfc); +} + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* + * The v2 of channel establishment uses a different RPC flow: + * `openchannel_init`, `openchannel_update`, && `openchannel_signed` + *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* + */ + +/* We call it circularly, til finished */ +static struct command_result * +perform_openchannel_update(struct multifundchannel_command *mfc); + +static struct command_result * +openchannel_update_returned(struct multifundchannel_destination *dest) +{ + struct multifundchannel_command *mfc = dest->mfc; + + --mfc->pending; + if (mfc->pending == 0) + return perform_openchannel_update(mfc); + else + return command_still_pending(mfc->cmd); +} + +static struct command_result * +openchannel_update_ok(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct multifundchannel_destination *dest) +{ + struct multifundchannel_command *mfc = dest->mfc; + const jsmntok_t *psbt_tok, *done_tok; + bool done; + + plugin_log(mfc->cmd->plugin, LOG_DBG, + "mfc %"PRIu64", dest %u: openchannel_update %s returned.", + mfc->id, dest->index, + node_id_to_hexstr(tmpctx, &dest->id)); + + assert(!dest->updated_psbt); + psbt_tok = json_get_member(buf, result, "psbt"); + if (!psbt_tok) + plugin_err(cmd->plugin, + "`openchannel_update` did not return " + "'psbt': %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + + if (!json_to_psbt(dest->mfc, buf, psbt_tok, &dest->updated_psbt)) + plugin_err(cmd->plugin, + "`openchannel_update` returned invalid " + "'psbt': %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + + /* Should we check that the channel id is correct? */ + + done_tok = json_get_member(buf, result, "commitments_secured"); + if (!done_tok) + plugin_err(cmd->plugin, + "`openchannel_update` failed to return " + "'commitments_secured': %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + + if (!json_to_bool(buf, done_tok, &done)) + plugin_err(cmd->plugin, + "`openchannel_update` returned invalid " + "'commitments_secured': %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + + if (done) { + const jsmntok_t *outnum_tok; + + outnum_tok = json_get_member(buf, result, "funding_outnum"); + if (!outnum_tok) + plugin_err(cmd->plugin, + "`openchannel_update` did not return " + "'funding_outnum': %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + + if (!json_to_number(buf, outnum_tok, &dest->outnum)) + plugin_err(cmd->plugin, + "`openchannel_update` returned invalid " + "'funding_outnum': %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + + /* It's possible they beat us to the SIGNED flag, + * in which case we just let that be the more senior + * state position */ + if (dest->state != MULTIFUNDCHANNEL_SIGNED) + dest->state = MULTIFUNDCHANNEL_SECURED; + } else + dest->state = MULTIFUNDCHANNEL_UPDATED; + + return openchannel_update_returned(dest); +} + +static struct command_result * +openchannel_update_err(struct command *cmd, + const char *buf, + const jsmntok_t *error, + struct multifundchannel_destination *dest) +{ + struct multifundchannel_command *mfc = dest->mfc; + const jsmntok_t *code_tok; + + plugin_err(mfc->cmd->plugin, + "mfc %"PRIu64", dest %u: " + "failed! `openchannel_update` %s: %.*s", + mfc->id, dest->index, + node_id_to_hexstr(tmpctx, &dest->id), + json_tok_full_len(error), json_tok_full(buf, error)); + + code_tok = json_get_member(buf, error, "code"); + if (!code_tok) + plugin_err(cmd->plugin, + "`openchannel_update` failure missing " + "`code`? %.*s", + json_tok_full_len(error), + json_tok_full(buf, error)); + if (!json_to_errcode(buf, code_tok, &dest->code)) + plugin_err(cmd->plugin, + "`openchannel_update` returned unparseable `code`? " + "%.*s", + json_tok_full_len(error), + json_tok_full(buf, error)); + + dest->state = MULTIFUNDCHANNEL_FAILED; + dest->error = json_strdup(mfc, buf, error); + + return openchannel_update_returned(dest); +} + +static void +openchannel_update_dest(struct multifundchannel_destination *dest) +{ + struct multifundchannel_command *mfc = dest->mfc; + struct command *cmd = mfc->cmd; + struct out_req *req; + + plugin_log(cmd->plugin, LOG_DBG, + "mfc %"PRIu64", dest %u: `openchannel_update` %s " + "with psbt %s", + mfc->id, dest->index, + node_id_to_hexstr(tmpctx, &dest->id), + type_to_string(tmpctx, struct wally_psbt, dest->psbt)); + + req = jsonrpc_request_start(cmd->plugin, + cmd, + "openchannel_update", + &openchannel_update_ok, + &openchannel_update_err, + dest); + json_add_channel_id(req->js, "channel_id", &dest->channel_id); + json_add_psbt(req->js, "psbt", dest->psbt); + + send_outreq(cmd->plugin, req); +} + +static struct command_result * +perform_openchannel_update(struct multifundchannel_command *mfc) +{ + size_t i, ready_count = 0; + + plugin_log(mfc->cmd->plugin, LOG_DBG, + "mfc %"PRIu64": parallel `openchannel_update`.", + mfc->id); + + /* First we check for failures/finished state */ + for (i = 0; i < tal_count(mfc->destinations); i++) { + struct multifundchannel_destination *dest; + dest = &mfc->destinations[i]; + + if (dest->state == MULTIFUNDCHANNEL_FAILED) + return redo_multiopenchannel(mfc, + "openchannel_update"); + + /* If any *one* is secured or signed, they should all + * be done. */ + if (dest->state == MULTIFUNDCHANNEL_SECURED || + dest->state == MULTIFUNDCHANNEL_SIGNED) { + ready_count++; + continue; + } + + assert(dest->state == MULTIFUNDCHANNEL_UPDATED || + dest->state == MULTIFUNDCHANNEL_STARTED); + } + + // FIXME: how many is the total count here? + if (ready_count == tal_count(mfc->destinations)) { + return command_still_pending(mfc->cmd); + } + + /* Then, we update the parent with every node's result */ + for (i = 0; i < tal_count(mfc->destinations); i++) { + struct multifundchannel_destination *dest; + dest = &mfc->destinations[i]; + + if (!update_parent_psbt(mfc, dest, dest->psbt, + dest->updated_psbt, + &mfc->psbt)) + return redo_multiopenchannel(mfc, + "openchannel_init_parent"); + /* Get everything sorted correctly */ + psbt_sort_by_serial_id(mfc->psbt); + + tal_free(dest->psbt); + dest->psbt = dest->updated_psbt; + dest->updated_psbt = NULL; + } + + /* Next we update the view of every destination with the + * parent viewset */ + for (i = 0; i < tal_count(mfc->destinations); i++) { + struct multifundchannel_destination *dest; + dest = &mfc->destinations[i]; + + if (!update_node_psbt(mfc, mfc->psbt, &dest->psbt)) + return redo_multiopenchannel(mfc, + "openchannel_init_node"); + } + + mfc->pending = tal_count(mfc->destinations); + for (i = 0; i < tal_count(mfc->destinations); i++) + openchannel_update_dest(&mfc->destinations[i]); + + assert(mfc->pending != 0); + return command_still_pending(mfc->cmd); +} + +static struct command_result * +after_openchannel_init(struct multifundchannel_command *mfc) +{ + unsigned int i; + + plugin_log(mfc->cmd->plugin, LOG_DBG, + "mfc %"PRIu64": parallel openchannel_init done.", + mfc->id); + + /* Check if any openchannel_init failed. */ + for (i = 0; i < tal_count(mfc->destinations); ++i) { + struct multifundchannel_destination *dest; + + dest = &mfc->destinations[i]; + + assert(dest->state == MULTIFUNDCHANNEL_STARTED + || dest->state == MULTIFUNDCHANNEL_START_FAILED); + + if (dest->state != MULTIFUNDCHANNEL_START_FAILED) + continue; + + /* One of them failed, oh no. */ + return redo_multiopenchannel(mfc, "openchannel_init"); + } + + /* We need to add the change output here, for now. Will + * remove when fundchannel flow is merged */ + if (mfc->change_needed) { + struct wally_psbt_output *out; + u16 serial_id; + + out = psbt_append_output(mfc->psbt, + mfc->change_scriptpubkey, + mfc->change_amount); + + serial_id = psbt_new_output_serial(mfc->psbt, TX_INITIATOR); + psbt_output_set_serial_id(mfc->psbt, out, serial_id); + } + + /* Now we stash the 'mfc' command, so when/if + * signature notifications start coming in, we'll catch them. */ + register_mfc(mfc); + + return perform_openchannel_update(mfc); +} + +static struct command_result * +openchannel_init_done(struct multifundchannel_destination *dest) +{ + struct multifundchannel_command *mfc = dest->mfc; + + --mfc->pending; + if (mfc->pending == 0) + return after_openchannel_init(mfc); + else + return command_still_pending(mfc->cmd); +} + +static struct command_result * +openchannel_init_ok(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct multifundchannel_destination *dest) +{ + struct multifundchannel_command *mfc = dest->mfc; + const jsmntok_t *psbt_tok; + const jsmntok_t *channel_id_tok; + const jsmntok_t *funding_serial_tok; + + plugin_log(mfc->cmd->plugin, LOG_DBG, + "mfc %"PRIu64", dest %u: openchannel_init %s done.", + mfc->id, dest->index, + node_id_to_hexstr(tmpctx, &dest->id)); + + /* We've got the PSBT and channel_id here */ + psbt_tok = json_get_member(buf, result, "psbt"); + if (!psbt_tok) + plugin_err(cmd->plugin, + "openchannel_init did not return " + "'psbt': %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + if (!json_to_psbt(dest->mfc, buf, psbt_tok, &dest->updated_psbt)) + plugin_err(cmd->plugin, + "openchannel_init returned invalid " + "'psbt': %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + + channel_id_tok = json_get_member(buf, result, "channel_id"); + if (!channel_id_tok) + plugin_err(cmd->plugin, + "openchannel_init did not return " + "'channel_id': %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + json_to_channel_id(buf, channel_id_tok, &dest->channel_id); + + funding_serial_tok = json_get_member(buf, result, + "funding_serial"); + if (!funding_serial_tok) + plugin_err(cmd->plugin, + "openchannel_init did not return " + "'funding_serial': %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + json_to_u64(buf, funding_serial_tok, &dest->funding_serial); + + dest->state = MULTIFUNDCHANNEL_STARTED; + + /* Port any updates onto 'parent' PSBT */ + if (!update_parent_psbt(dest->mfc, dest, dest->psbt, + dest->updated_psbt, &mfc->psbt)) { + dest->state = MULTIFUNDCHANNEL_FAILED; + dest->error = "Unable to update parent with node's PSBT"; + } + + /* Clone updated-psbt to psbt, so original changeset + * will be empty, but tallocate it so we can leave tal_free + * logic in `perform_openchannel_update` the same. */ + tal_wally_start(); + wally_psbt_clone_alloc(dest->updated_psbt, 0, &dest->psbt); + tal_wally_end(tal_steal(mfc, dest->updated_psbt)); + return openchannel_init_done(dest); +} + +static struct command_result * +openchannel_init_err(struct command *cmd, + const char *buf, + const jsmntok_t *error, + struct multifundchannel_destination *dest) +{ + struct multifundchannel_command *mfc = dest->mfc; + const jsmntok_t *code_tok; + + plugin_err(mfc->cmd->plugin, + "mfc %"PRIu64", dest %u: " + "failed! openchannel_init %s: %.*s.", + mfc->id, dest->index, + node_id_to_hexstr(tmpctx, &dest->id), + json_tok_full_len(error), + json_tok_full(buf, error)); + + code_tok = json_get_member(buf, error, "code"); + if (!code_tok) + plugin_err(cmd->plugin, + "`openchannel_init` failure did not have `code`? " + "%.*s", + json_tok_full_len(error), + json_tok_full(buf, error)); + if (!json_to_errcode(buf, code_tok, &dest->code)) + plugin_err(cmd->plugin, + "`openchannel_init` has unparseable `code`? " + "%.*s", + json_tok_full_len(code_tok), + json_tok_full(buf, code_tok)); + + dest->state = MULTIFUNDCHANNEL_START_FAILED; + dest->error = json_strdup(dest->mfc, buf, error); + + return openchannel_init_done(dest); +} + +struct command_result * +openchannel_init_dest(struct multifundchannel_destination *dest) +{ + struct multifundchannel_command *mfc = dest->mfc; + struct command *cmd = mfc->cmd; + struct out_req *req; + + plugin_log(cmd->plugin, LOG_DBG, + "mfc %"PRIu64", dest %u: openchannel_init %s.", + mfc->id, dest->index, + node_id_to_hexstr(tmpctx, &dest->id)); + + req = jsonrpc_request_start(cmd->plugin, cmd, + "openchannel_init", + &openchannel_init_ok, + &openchannel_init_err, + dest); + + json_add_node_id(req->js, "id", &dest->id); + assert(!dest->all); + json_add_string(req->js, "amount", + fmt_amount_sat(tmpctx, &dest->amount)); + + /* Copy the original parent down */ + tal_wally_start(); + wally_psbt_clone_alloc(mfc->psbt, 0, &dest->psbt); + tal_wally_end(tal_steal(mfc, dest->psbt)); + + json_add_psbt(req->js, "initialpsbt", dest->psbt); + if (mfc->cmtmt_feerate_str) + json_add_string(req->js, "commitment_feerate", + mfc->cmtmt_feerate_str); + if (mfc->feerate_str) { + json_add_string(req->js, "funding_feerate", + mfc->feerate_str); + + /* If there's no commitment feerate provided, we assume + * that the same feerate is to be used on both. This mimics + * the behavior of the old-style feerate stuffs */ + if (!mfc->cmtmt_feerate_str) + json_add_string(req->js, "commitment_feerate", + mfc->feerate_str); + } + json_add_bool(req->js, "announce", dest->announce); + if (dest->close_to_str) + json_add_string(req->js, "close_to", dest->close_to_str); + + if (amount_msat_greater(dest->push_msat, AMOUNT_MSAT(0))) + plugin_log(cmd->plugin, LOG_INFORM, + "Using openchannel for %s open, " + "ignoring `push_msat` of %s", + node_id_to_hexstr(tmpctx, &dest->id), + type_to_string(tmpctx, struct amount_msat, + &dest->push_msat)); + + return send_outreq(cmd->plugin, req); +} + +void openchannel_init(struct plugin *p, const char *b, const jsmntok_t *t) +{ + /* Initialize our list! */ + list_head_init(&mfc_commands); +} + +const struct plugin_notification openchannel_notifs[] = { + { + "openchannel_peer_sigs", + json_peer_sigs, + } +}; +const size_t num_openchannel_notifs = ARRAY_SIZE(openchannel_notifs); diff --git a/plugins/spender/openchannel.h b/plugins/spender/openchannel.h new file mode 100644 index 000000000..16ea40df6 --- /dev/null +++ b/plugins/spender/openchannel.h @@ -0,0 +1,16 @@ +#ifndef LIGHTNING_PLUGINS_SPENDER_OPENCHANNEL_H +#define LIGHTNING_PLUGINS_SPENDER_OPENCHANNEL_H +#include "config.h" +#include + +struct wally_psbt; + +extern const struct plugin_notification openchannel_notifs[]; +extern const size_t num_openchannel_notifs; + +struct command_result * +openchannel_init_dest(struct multifundchannel_destination *dest); + +void openchannel_init(struct plugin *p, const char *b, + const jsmntok_t *t); +#endif /* LIGHTNING_PLUGINS_SPENDER_OPENCHANNEL_H */