diff --git a/lightningd/Makefile b/lightningd/Makefile index d1d7acc15..93b75471a 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -8,6 +8,7 @@ LIGHTNINGD_SRC := \ lightningd/closing_control.c \ lightningd/coin_mvts.c \ lightningd/connect_control.c \ + lightningd/dual_open_control.c \ lightningd/onion_message.c \ lightningd/gossip_control.c \ lightningd/gossip_msg.c \ @@ -100,6 +101,7 @@ LIGHTNINGD_COMMON_OBJS := \ common/penalty_base.o \ common/per_peer_state.o \ common/permute_tx.o \ + common/psbt_open.o \ common/pseudorand.o \ common/random_select.o \ common/setup.o \ diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c new file mode 100644 index 000000000..9554aa7d9 --- /dev/null +++ b/lightningd/dual_open_control.c @@ -0,0 +1,1038 @@ +/* This is the lightningd handler for messages to/from various + * dualopend subdaemons. It manages the callbacks and database + * saves and funding tx watching for a channel open */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if EXPERIMENTAL_FEATURES +struct commit_rcvd { + struct channel *channel; + struct channel_id cid; + struct per_peer_state *pps; + u8 *commitment_msg; + struct uncommitted_channel *uc; +}; + +static const struct witness_stack ** +psbt_to_witness_stacks(const tal_t *ctx, struct log *log, + const struct wally_psbt *psbt, enum side opener) +{ + size_t stack_index; + u16 serial_id; + const struct witness_stack **stacks + = tal_arr(ctx, const struct witness_stack *, psbt->num_inputs); + + stack_index = 0; + for (size_t i = 0; i < psbt->num_inputs; i++) { + if (!psbt_get_serial_id(&psbt->inputs[i].unknowns, &serial_id)) { + log_broken(log, "dual funding PSBT must have serial_id for each " + "input, none found for input %zu", i); + return NULL; + } + + + if (serial_id % 2 == opener) { + struct wally_tx_witness_stack *wtx_s = + psbt->inputs[i].final_witness; + struct witness_stack *stack = + tal(stacks, struct witness_stack); + /* Convert the wally_tx_witness_stack to + * a witness_stack entry */ + stack->witness_element = + tal_arr(stack, struct witness_element *, + wtx_s->num_items); + for (size_t j = 0; j < tal_count(stack->witness_element); j++) { + stack->witness_element[j] = tal(stack, + struct witness_element); + stack->witness_element[j]->witness = + tal_dup_arr(stack, u8, + wtx_s->items[j].witness, + wtx_s->items[j].witness_len, + 0); + + } + + stacks[stack_index++] = stack; + } + + } + + if (stack_index == 0) + return tal_free(stacks); + + tal_resize(&stacks, stack_index); + return stacks; +} + +static bool psbt_side_finalized(struct log *log, struct wally_psbt *psbt, enum side opener) +{ + u16 serial_id; + for (size_t i = 0; i < psbt->num_inputs; i++) { + if (!psbt_get_serial_id(&psbt->inputs[i].unknowns, &serial_id)) { + log_broken(log, "dual funding PSBT must have serial_id for each " + "input, none found for input %zu", i); + return false; + } + /* It's our input if parity matches -- this shorthand + * works because LOCAL == 0. If the parity is even and + * we're the opener then it's ours; if the parity is odd + * and the REMOTE's the opener (opener == 1), then it's also + * ours. */ + if (serial_id % 2 == opener) { + if (!psbt->inputs[i].final_witness || + psbt->inputs[i].final_witness->num_items == 0) + return false; + } + } + return true; +} + +static void handle_signed_psbt(struct lightningd *ld, + const struct wally_psbt *psbt, + struct commit_rcvd *rcvd) +{ + const struct witness_stack **ws = + psbt_to_witness_stacks(tmpctx, ld->log, psbt, REMOTE); + + const u8 *fwd_msg_2; + + /* We've already confirmed that all of the supplied info is good, + * so now go ahead and create a tx_signatures msg. + * We'll pass the tx_sigs msg and the already-created + * commitment_signed tx to channeld, who will send + * both of them to the peer. */ + fwd_msg_2 = towire_tx_signatures(rcvd, &rcvd->channel->cid, + &rcvd->channel->funding_txid, + ws); + + channel_watch_funding(ld, rcvd->channel); + + peer_start_channeld(rcvd->channel, + rcvd->pps, + rcvd->commitment_msg, + fwd_msg_2, false); + tal_free(rcvd->uc); +} + +/* ~Map of the Territory~ + * + * openchannel hook + - reserveinputs feerate [{"amt": amt, "script": ""}] excludecommon=true -> psbt + -> psbt_set + * + * openchannel_changed hook + - psbt --> most recent + -> psbt_set (if same as orig) | complete flag + * + * openchannel_sign hook + - signpsbt psbt -> partially_signed_psbt + -> partially_signed_psbt +*/ +struct openchannel2_payload { + struct subd *dualopend; + struct node_id peer_id; + struct amount_sat their_funding; + struct amount_sat dust_limit_satoshis; + struct amount_msat max_htlc_value_in_flight_msat; + struct amount_msat htlc_minimum_msat; + u32 feerate_per_kw_funding; + u32 feerate_per_kw; + u16 to_self_delay; + u16 max_accepted_htlcs; + u8 channel_flags; + u32 locktime; + u8 *shutdown_scriptpubkey; + /* FIXME: include the podle? */ + + struct amount_sat accepter_funding; + struct wally_psbt *psbt; + const u8 *our_shutdown_scriptpubkey; +}; + +static void +openchannel2_hook_serialize(struct openchannel2_payload *payload, + struct json_stream *stream) +{ + json_object_start(stream, "openchannel2"); + json_add_node_id(stream, "id", &payload->peer_id); + json_add_amount_sat_only(stream, "their_funding", + payload->their_funding); + json_add_amount_sat_only(stream, "dust_limit_satoshis", + payload->dust_limit_satoshis); + json_add_amount_msat_only(stream, "max_htlc_value_in_flight_msat", + payload->max_htlc_value_in_flight_msat); + json_add_amount_msat_only(stream, "htlc_minimum_msat", + payload->htlc_minimum_msat); + json_add_num(stream, "feerate_per_kw_funding", + payload->feerate_per_kw_funding); + json_add_num(stream, "feerate_per_kw", payload->feerate_per_kw); + json_add_num(stream, "to_self_delay", payload->to_self_delay); + json_add_num(stream, "max_accepted_htlcs", payload->max_accepted_htlcs); + json_add_num(stream, "channel_flags", payload->channel_flags); + json_add_num(stream, "locktime", payload->locktime); + if (tal_bytelen(payload->shutdown_scriptpubkey) != 0) + json_add_hex_talarr(stream, "shutdown_scriptpubkey", + payload->shutdown_scriptpubkey); + /* FIXME: include the podle? */ + json_object_end(stream); +} + +struct openchannel2_psbt_payload { + struct subd *dualopend; + struct wally_psbt *psbt; + struct commit_rcvd *rcvd; + struct lightningd *ld; +}; + +static void +openchannel2_changed_hook_serialize(struct openchannel2_psbt_payload *payload, + struct json_stream *stream) +{ + json_object_start(stream, "openchannel2_changed"); + json_add_psbt(stream, "psbt", payload->psbt); + json_add_string(stream, "channel_id", + type_to_string(tmpctx, struct channel_id, + &payload->rcvd->channel->cid)); + json_object_end(stream); +} + +static void +openchannel2_sign_hook_serialize(struct openchannel2_psbt_payload *payload, + struct json_stream *stream) +{ + json_object_start(stream, "openchannel2_sign"); + json_add_psbt(stream, "psbt", payload->psbt); + json_add_string(stream, "channel_id", + type_to_string(tmpctx, struct channel_id, + &payload->rcvd->channel->cid)); + json_object_end(stream); +} + +static const u8 *hook_extract_shutdown_script(struct subd* dualopend, + const char *buffer, + const jsmntok_t *toks) +{ + const u8 *close_to_script; + enum address_parse_result parse_res; + + if (!buffer) + return NULL; + + const jsmntok_t *t = json_get_member(buffer, toks, "result"); + if (!t) + fatal("Plugin must return a 'result'" + "%.*s", toks[0].end - toks[0].start, + buffer + toks[0].start); + + if (!json_tok_streq(buffer, t, "continue")) { + char *errmsg = "Client error. Unable to continue"; + subd_send_msg(dualopend, + take(towire_dual_open_fail(NULL, errmsg))); + return NULL; + } + + const jsmntok_t *close_to_tok = json_get_member(buffer, toks, "close_to"); + if (!close_to_tok) + return NULL; + + parse_res = json_to_address_scriptpubkey(tmpctx, chainparams, buffer, + close_to_tok, &close_to_script); + switch (parse_res) { + case ADDRESS_PARSE_UNRECOGNIZED: + fatal("Plugin returned an invalid response to the" + " openchannel2.close_to hook: %.*s", + t->end - t->start, buffer + t->start); + case ADDRESS_PARSE_WRONG_NETWORK: + fatal("Plugin returned invalid response to the" + " openchannel2.close_to hook: address %s is" + " not on network %s", + tal_hex(NULL, close_to_script), + chainparams->network_name); + case ADDRESS_PARSE_SUCCESS: + return close_to_script; + } + + return NULL; +} + + +static bool +hook_extract_psbt(const tal_t *ctx, struct subd *dualopend, const char *buffer, + const jsmntok_t *toks, char *hook_name, + bool allow_empty, + struct wally_psbt **out) +{ + struct wally_psbt *returned_psbt; + + if (!buffer) + fatal("Plugin must return a valid response to %s", hook_name); + + const jsmntok_t *t = json_get_member(buffer, toks, "result"); + if (!t) + fatal("Plugin must return a 'result' to %s" + "%.*s", hook_name, toks[0].end - toks[0].start, + buffer + toks[0].start); + + if (!json_tok_streq(buffer, t, "continue")) { + /* dualopend might have closed if we're on the signed round */ + if (dualopend) { + char *errmsg = "Client error. Unable to continue"; + subd_send_msg(dualopend, + take(towire_dual_open_fail(NULL, errmsg))); + } + return false; + } + + const jsmntok_t *psbt_tok = json_get_member(buffer, toks, "psbt"); + if (!psbt_tok) { + if (!allow_empty) + fatal("Plugin must return a 'psbt' to a 'continue'd" + "%s %.*s", hook_name, + toks[0].end - toks[0].start, + buffer + toks[0].start); + *out = NULL; + return true; + } + + if (!json_to_psbt(ctx, buffer, psbt_tok, &returned_psbt)) + fatal("Plugin must return a valid 'psbt' to a 'continue'd" + "%s %.*s", hook_name, + toks[0].end - toks[0].start, + buffer + toks[0].start); + + *out = returned_psbt; + return true; +} + +/* The field is *always* assumed msats, as that's the unit + * amount we're transitioning our API over to. A 'xxxsat' + * unit will be interpreted correctly, but a value given + * without a unit will always be interpreted as msats */ +static bool +hook_extract_amount(struct subd *dualopend, + const char *buffer, + const jsmntok_t *toks, + char *field_name, + struct amount_sat *amount) +{ + struct amount_msat msats; + + if (!buffer) + return false; + + const jsmntok_t *t = json_get_member(buffer, toks, "result"); + if (!t) + fatal("Plugin must return a 'result' " + " %.*s", toks[0].end - toks[0].start, + buffer + toks[0].start); + + if (!json_tok_streq(buffer, t, "continue")) { + char *errmsg = "Client error. Unable to continue"; + subd_send_msg(dualopend, + take(towire_dual_open_fail(NULL, errmsg))); + return false; + } + + /* If there's no amount_sat field, that's ok */ + const jsmntok_t *amt_tok = json_get_member(buffer, toks, field_name); + if (!amt_tok) { + *amount = AMOUNT_SAT(0); + return true; + } + + if (!json_to_msat(buffer, amt_tok, &msats)) + fatal("Plugin must return a valid '%s' to a 'continue'd" + " %.*s", field_name, + toks[0].end - toks[0].start, + buffer + toks[0].start); + + *amount = amount_msat_to_sat_round_down(msats); + return true; +} + +#define CHECK_CHANGES(set, dir) \ + do { \ + for (size_t i = 0; i < tal_count(set); i++) { \ + ok = psbt_get_serial_id(&set[i].dir.unknowns, &serial_id); \ + assert(ok); \ + if (serial_id % 2 != opener_side) \ + return true; \ + } \ + } while (false) \ + +static bool psbt_side_contribs_changed(struct wally_psbt *orig, + struct wally_psbt *new, + enum side opener_side) +{ + struct input_set *added_in, *rm_in; + struct output_set *added_out, *rm_out; + u16 serial_id; + bool ok; + + if (!psbt_has_diff(tmpctx, orig, new, + &added_in, &rm_in, + &added_out, &rm_out)) + return false; + + /* If there were *any* changes, then the answer to the 'both sides' + * question is "yes, there were changes" */ + if (opener_side == NUM_SIDES) + return true; + + /* Check that none of the included updates have a serial + * id that's the peer's parity */ + CHECK_CHANGES(added_in, input); + CHECK_CHANGES(rm_in, input); + CHECK_CHANGES(added_out, output); + CHECK_CHANGES(rm_out, output); + + return false; +} + +/* Adds serials indiscriminately to any input/output that doesn't + * have one yet */ +static void psbt_add_serials(struct wally_psbt *psbt, enum side opener) +{ + u16 serial_id; + const u64 serial_space = 100000; + for (size_t i = 0; i < psbt->num_inputs; i++) { + /* Skip ones that already have a serial id */ + if (psbt_get_serial_id(&psbt->inputs[i].unknowns, &serial_id)) + continue; + + while ((serial_id = pseudorand(serial_space)) % 2 != opener || + psbt_has_serial_input(psbt, serial_id)) { + /* keep going; */ + } + psbt_input_add_serial_id(&psbt->inputs[i], serial_id); + } + for (size_t i = 0; i < psbt->num_outputs; i++) { + /* Skip ones that already have a serial id */ + if (psbt_get_serial_id(&psbt->outputs[i].unknowns, &serial_id)) + continue; + + while ((serial_id = pseudorand(serial_space)) % 2 != opener || + psbt_has_serial_output(psbt, serial_id)) { + /* keep going; */ + } + psbt_output_add_serial_id(&psbt->outputs[i], serial_id); + } +} + +/* dualopend dies? Remove dualopend ptr from payload */ +static void openchannel2_remove_dualopend(struct subd *dualopend, + struct openchannel2_payload *payload) +{ + assert(payload->dualopend == dualopend); + payload->dualopend = NULL; +} + +static bool +openchannel2_hook_deserialize(struct openchannel2_payload *payload, + const char *buffer, + const jsmntok_t *toks) +{ + struct subd *dualopend = payload->dualopend; + + /* If our daemon died, we're done */ + if (!dualopend) { + tal_free(payload); + return false; + } + + if (!hook_extract_psbt(payload, dualopend, buffer, toks, + "openchannel2", true, &payload->psbt)) + return false; + + payload->our_shutdown_scriptpubkey = + hook_extract_shutdown_script(dualopend, buffer, toks); + + /* Add a serial_id to everything that doesn't have one yet */ + if (payload->psbt) + psbt_add_serials(payload->psbt, REMOTE); + + if (payload->psbt && !psbt_has_required_fields(payload->psbt)) + fatal("Plugin supplied PSBT that's missing required fields. %s", + type_to_string(tmpctx, struct wally_psbt, payload->psbt)); + + if (!hook_extract_amount(dualopend, buffer, toks, "accepter_funding_msat", + &payload->accepter_funding)) + fatal("Plugin failed to supply accepter_funding_msat field"); + + if (!payload->psbt && + !amount_sat_eq(payload->accepter_funding, AMOUNT_SAT(0))) { + /* Gotta give a PSBT if you set the accepter_funding amount */ + return false; + } + + return true; +} + +static void +openchannel2_hook_cb(struct openchannel2_payload *payload STEALS) +{ + struct subd *dualopend = payload->dualopend; + + /* Free payload regardless of what happens next */ + tal_steal(tmpctx, payload); + + /* If our daemon died, we're done */ + if (!dualopend) + return; + + tal_del_destructor2(dualopend, openchannel2_remove_dualopend, payload); + + /* If there's no plugin, the psbt will be NULL. We should pass an empty + * PSBT over, in this case */ + subd_send_msg(dualopend, + take(towire_dual_open_got_offer_reply(NULL, + payload->accepter_funding, + payload->psbt, + payload->our_shutdown_scriptpubkey))); +} + +/* dualopend dies? Remove dualopend ptr from payload */ +static void openchannel2_psbt_remove_dualopend(struct subd *dualopend, + struct openchannel2_psbt_payload *payload) +{ + assert(payload->dualopend == dualopend); + payload->dualopend = NULL; +} + +static void +openchannel2_changed_hook_cb(struct openchannel2_psbt_payload *payload STEALS) +{ + struct subd *dualopend = payload->dualopend; + + /* Free payload regardless of what happens next */ + tal_steal(tmpctx, payload); + + /* If our daemon died, we're done */ + if (!dualopend) + return; + + tal_del_destructor2(dualopend, + openchannel2_psbt_remove_dualopend, + payload); + + subd_send_msg(dualopend, + take(towire_dual_open_psbt_changed(NULL, + &payload->rcvd->cid, + payload->psbt))); +} + +static bool +openchannel2_psbt_deserialize(struct openchannel2_psbt_payload *payload, + const char *buffer, const jsmntok_t *toks) +{ + struct subd *dualopend = payload->dualopend; + struct wally_psbt *psbt; + + if (!hook_extract_psbt(NULL, dualopend, buffer, + toks, "openchannel2_sign", + false, &psbt)) + return false; + + if (!psbt_has_required_fields(psbt)) + fatal("Plugin supplied PSBT that's missing required fields. %s", + type_to_string(tmpctx, struct wally_psbt, psbt)); + + /* Verify that inputs/outputs are the same. Note that this is a + * 'de minimus' check -- we just look at serial_ids. If you've + * totally managled the data here but left the serial_ids intact, + * you'll get a failure back from the peer when you send + * commitment sigs */ + if (psbt_side_contribs_changed(payload->psbt, psbt, NUM_SIDES)) + fatal("Plugin must not change psbt input/output set. " + "orig: %s. updated: %s", + type_to_string(tmpctx, struct wally_psbt, + payload->psbt), + type_to_string(tmpctx, struct wally_psbt, + psbt)); + + if (payload->psbt) + tal_free(payload->psbt); + payload->psbt = tal_steal(payload, psbt); + return true; +} + +static void +openchannel2_sign_hook_cb(struct openchannel2_psbt_payload *payload STEALS) +{ + /* Free payload regardless of what happens next */ + tal_steal(tmpctx, payload); + + /* Finalize it, if not already. It shouldn't work entirely */ + psbt_finalize(payload->psbt, true); + + if (!psbt_side_finalized(payload->ld->log, payload->psbt, REMOTE)) + fatal("Plugin must return a 'psbt' with signatures for their inputs" + " %s", type_to_string(tmpctx, struct wally_psbt, payload->psbt)); + + handle_signed_psbt(payload->ld, payload->psbt, payload->rcvd); +} + +REGISTER_PLUGIN_HOOK(openchannel2, + openchannel2_hook_deserialize, + openchannel2_hook_cb, + openchannel2_hook_serialize, + struct openchannel2_payload *); + +REGISTER_PLUGIN_HOOK(openchannel2_changed, + openchannel2_psbt_deserialize, + openchannel2_changed_hook_cb, + openchannel2_changed_hook_serialize, + struct openchannel2_psbt_payload *); + +REGISTER_PLUGIN_HOOK(openchannel2_sign, + openchannel2_psbt_deserialize, + openchannel2_sign_hook_cb, + openchannel2_sign_hook_serialize, + struct openchannel2_psbt_payload *); + +/* Steals fields from uncommitted_channel: returns NULL if can't generate a + * key for this channel (shouldn't happen!). */ +static struct channel * +wallet_commit_channel(struct lightningd *ld, + struct uncommitted_channel *uc, + struct channel_id *cid, + struct bitcoin_tx *remote_commit, + struct bitcoin_signature *remote_commit_sig, + const struct bitcoin_txid *funding_txid, + u16 funding_outnum, + struct amount_sat total_funding, + struct amount_sat our_funding, + u8 channel_flags, + struct channel_info *channel_info, + u32 feerate, + enum side opener, + const u8 *our_upfront_shutdown_script, + const u8 *remote_upfront_shutdown_script) +{ + struct channel *channel; + s64 final_key_idx; + bool option_static_remotekey; + bool option_anchor_outputs; + struct amount_msat our_msat; + + /* Get a key to use for closing outputs from this tx */ + final_key_idx = wallet_get_newindex(ld); + if (final_key_idx == -1) { + log_broken(uc->log, "Can't get final key index"); + return NULL; + } + + if (!amount_sat_to_msat(&our_msat, our_funding)) { + log_broken(uc->log, "Unable to convert funds"); + return NULL; + } + + channel_info->fee_states = new_fee_states(uc, opener, &feerate); + + /* old_remote_per_commit not valid yet, copy valid one. */ + channel_info->old_remote_per_commit = channel_info->remote_per_commit; + + /* BOLT-a12da24dd0102c170365124782b46d9710950ac1 #2: + * 1. type: 35 (`funding_signed`) + * 2. data: + * * [`channel_id`:`channel_id`] + * * [`signature`:`signature`] + * + * #### Requirements + * + * Both peers: + * - if `option_static_remotekey` or `option_anchor_outputs` was negotiated: + * - `option_static_remotekey` or `option_anchor_outputs` applies to all commitment + * transactions + * - otherwise: + * - `option_static_remotekey` or `option_anchor_outputs` does not apply to any commitment + * transactions + */ + /* i.e. We set it now for the channel permanently. */ + option_static_remotekey + = feature_negotiated(ld->our_features, + uc->peer->their_features, + OPT_STATIC_REMOTEKEY); + + option_anchor_outputs + = feature_negotiated(ld->our_features, + uc->peer->their_features, + OPT_ANCHOR_OUTPUTS); + + channel = new_channel(uc->peer, uc->dbid, + NULL, /* No shachain yet */ + CHANNELD_AWAITING_LOCKIN, + opener, + uc->log, + take(uc->transient_billboard), + channel_flags, + &uc->our_config, + uc->minimum_depth, + 1, 1, 0, + funding_txid, + funding_outnum, + total_funding, + AMOUNT_MSAT(0), + our_funding, + false, /* !remote_funding_locked */ + NULL, /* no scid yet */ + cid, + /* The three arguments below are msatoshi_to_us, + * msatoshi_to_us_min, and msatoshi_to_us_max. + * Because, this is a newly-funded channel, + * all three are same value. */ + our_msat, + our_msat, /* msat_to_us_min */ + our_msat, /* msat_to_us_max */ + remote_commit, + remote_commit_sig, + NULL, /* No HTLC sigs yet */ + channel_info, + NULL, /* No shutdown_scriptpubkey[REMOTE] yet */ + our_upfront_shutdown_script, + final_key_idx, false, + NULL, /* No commit sent yet */ + /* If we're fundee, could be a little before this + * in theory, but it's only used for timing out. */ + get_block_height(ld->topology), + feerate, feerate, + /* We are connected */ + true, + &uc->local_basepoints, + &uc->local_funding_pubkey, + NULL, + ld->config.fee_base, + ld->config.fee_per_satoshi, + remote_upfront_shutdown_script, + option_static_remotekey, + option_anchor_outputs); + + /* Now we finally put it in the database. */ + wallet_channel_insert(ld->wallet, channel); + + return channel; +} + +static void accepter_commit_received(struct subd *dualopend, + struct uncommitted_channel *uc, + const int *fds, + const u8 *msg) +{ + struct openchannel2_psbt_payload *payload; + + struct lightningd *ld = dualopend->ld; + struct channel_info channel_info; + struct bitcoin_tx *remote_commit; + struct bitcoin_signature remote_commit_sig; + struct channel_id cid; + struct bitcoin_txid funding_txid; + struct per_peer_state *pps; + u16 funding_outnum; + u32 feerate; + struct amount_sat total_funding, funding_ours, channel_reserve; + u8 channel_flags, *remote_upfront_shutdown_script, + *local_upfront_shutdown_script, *commitment_msg; + struct penalty_base *pbase; + struct wally_psbt *psbt; + + payload = tal(uc, struct openchannel2_psbt_payload); + payload->rcvd = tal(payload, struct commit_rcvd); + + /* This is a new channel_info.their_config so set its ID to 0 */ + channel_info.their_config.id = 0; + + if (!fromwire_dual_open_commit_rcvd(tmpctx, msg, + &channel_info.their_config, + &remote_commit, + &pbase, + &remote_commit_sig, + &psbt, + &cid, + &pps, + &channel_info.theirbase.revocation, + &channel_info.theirbase.payment, + &channel_info.theirbase.htlc, + &channel_info.theirbase.delayed_payment, + &channel_info.remote_per_commit, + &channel_info.remote_fundingkey, + &funding_txid, + &funding_outnum, + &total_funding, + &funding_ours, + &channel_flags, + &feerate, + &commitment_msg, + &channel_reserve, + &local_upfront_shutdown_script, + &remote_upfront_shutdown_script)) { + log_broken(uc->log, "bad WIRE_DUAL_OPEN_COMMIT_RCVD %s", + tal_hex(msg, msg)); + uncommitted_channel_disconnect(uc, LOG_BROKEN, "bad WIRE_DUAL_OPEN_COMMIT_RCVD"); + close(fds[0]); + close(fds[1]); + close(fds[3]); + goto failed; + } + + per_peer_state_set_fds_arr(pps, fds); + payload->psbt = tal_steal(payload, psbt); + payload->rcvd->pps = tal_steal(payload, pps); + payload->rcvd->commitment_msg = tal_steal(payload, commitment_msg); + payload->ld = ld; + + if (peer_active_channel(uc->peer)) { + uncommitted_channel_disconnect(uc, LOG_BROKEN, "already have active channel"); + goto failed; + } + + payload->rcvd->channel = + wallet_commit_channel(ld, uc, + &cid, + remote_commit, + &remote_commit_sig, + &funding_txid, + funding_outnum, + total_funding, + funding_ours, + channel_flags, + &channel_info, + feerate, + REMOTE, + local_upfront_shutdown_script, + remote_upfront_shutdown_script); + + if (!payload->rcvd->channel) { + uncommitted_channel_disconnect(uc, LOG_BROKEN, "commit channel failed"); + goto failed; + } + + if (pbase) + wallet_penalty_base_add(ld->wallet, payload->rcvd->channel->dbid, + pbase); + + /* dualopend is going away! */ + /* We steal onto `NULL` because `payload` is tal'd off of `uc`; + * we free `uc` at the end though */ + payload->rcvd->uc = tal_steal(NULL, uc); + + /* We call out to our hook friend who will provide signatures for us! */ + plugin_hook_call_openchannel2_sign(ld, payload); + + /* We release the things here; dualopend is going away ?? */ + subd_release_channel(dualopend, uc); + uc->open_daemon = NULL; + return; + +failed: + subd_release_channel(dualopend, uc); + uc->open_daemon = NULL; + tal_free(uc); +} + +static void accepter_psbt_changed(struct subd *dualopend, + const u8 *msg) +{ + struct openchannel2_psbt_payload *payload = + tal(dualopend, struct openchannel2_psbt_payload); + payload->dualopend = dualopend; + payload->psbt = NULL; + payload->rcvd = tal(payload, struct commit_rcvd); + + if (!fromwire_dual_open_psbt_changed(payload, msg, + &payload->rcvd->cid, + &payload->psbt)) { + log_broken(dualopend->log, "Malformed dual_open_psbt_changed %s", + tal_hex(tmpctx, msg)); + tal_free(dualopend); + return; + } + + tal_add_destructor2(dualopend, openchannel2_psbt_remove_dualopend, payload); + plugin_hook_call_openchannel2_changed(dualopend->ld, payload); +} + +static void accepter_got_offer(struct subd *dualopend, + struct uncommitted_channel *uc, + const u8 *msg) +{ + struct openchannel2_payload *payload; + + if (peer_active_channel(uc->peer)) { + subd_send_msg(dualopend, + take(towire_dual_open_fail(NULL, "Already have active channel"))); + return; + } + + payload = tal(dualopend, struct openchannel2_payload); + payload->dualopend = dualopend; + payload->psbt = NULL; + payload->accepter_funding = AMOUNT_SAT(0); + payload->our_shutdown_scriptpubkey = NULL; + payload->peer_id = uc->peer->id; + + if (!fromwire_dual_open_got_offer(payload, msg, + &payload->their_funding, + &payload->dust_limit_satoshis, + &payload->max_htlc_value_in_flight_msat, + &payload->htlc_minimum_msat, + &payload->feerate_per_kw_funding, + &payload->feerate_per_kw, + &payload->to_self_delay, + &payload->max_accepted_htlcs, + &payload->channel_flags, + &payload->locktime, + &payload->shutdown_scriptpubkey)) { + log_broken(uc->log, "Malformed dual_open_got_offer %s", + tal_hex(tmpctx, msg)); + tal_free(dualopend); + return; + } + + tal_add_destructor2(dualopend, openchannel2_remove_dualopend, payload); + plugin_hook_call_openchannel2(dualopend->ld, payload); +} + +static unsigned int dual_opend_msg(struct subd *dualopend, + const u8 *msg, const int *fds) +{ + enum dualopend_wire t = fromwire_peektype(msg); + struct uncommitted_channel *uc = dualopend->channel; + + switch (t) { + case WIRE_DUAL_OPEN_GOT_OFFER: + accepter_got_offer(dualopend, uc, msg); + return 0; + case WIRE_DUAL_OPEN_PSBT_CHANGED: + accepter_psbt_changed(dualopend, msg); + return 0; + case WIRE_DUAL_OPEN_COMMIT_RCVD: + if (tal_count(fds) != 3) + return 3; + accepter_commit_received(dualopend, uc, fds, msg); + return 0; + case WIRE_DUAL_OPEN_FAILED: + case WIRE_DUAL_OPEN_DEV_MEMLEAK_REPLY: + + /* Messages we send */ + case WIRE_DUAL_OPEN_INIT: + case WIRE_DUAL_OPEN_GOT_OFFER_REPLY: + case WIRE_DUAL_OPEN_FAIL: + case WIRE_DUAL_OPEN_DEV_MEMLEAK: + break; + } + + switch ((enum common_wire)t) { +#if DEVELOPER + case WIRE_CUSTOMMSG_IN: + handle_custommsg_in(dualopend->ld, dualopend->node_id, msg); + return 0; +#else + case WIRE_CUSTOMMSG_IN: +#endif + /* We send these. */ + case WIRE_CUSTOMMSG_OUT: + break; + } + + log_broken(dualopend->log, "Unexpected msg %s: %s", + dualopend_wire_name(t), tal_hex(tmpctx, msg)); + tal_free(dualopend); + return 0; +} + +void peer_start_dualopend(struct peer *peer, + struct per_peer_state *pps, + const u8 *send_msg) +{ + + int hsmfd; + u32 max_to_self_delay; + struct amount_msat min_effective_htlc_capacity; + struct uncommitted_channel *uc; + const u8 *msg; + + assert(!peer->uncommitted_channel); + + uc = peer->uncommitted_channel = new_uncommitted_channel(peer); + + hsmfd = hsm_get_client_fd(peer->ld, &uc->peer->id, uc->dbid, + HSM_CAP_COMMITMENT_POINT + | HSM_CAP_SIGN_REMOTE_TX); + + uc->open_daemon = new_channel_subd(peer->ld, + "lightning_dualopend", + uc, &peer->id, uc->log, + true, dualopend_wire_name, + dual_opend_msg, + opend_channel_errmsg, + opend_channel_set_billboard, + take(&pps->peer_fd), + take(&pps->gossip_fd), + take(&pps->gossip_store_fd), + take(&hsmfd), NULL); + if (!uc->open_daemon) { + uncommitted_channel_disconnect(uc, LOG_BROKEN, + tal_fmt(tmpctx, + "Running lightning_dualopend: %s", + strerror(errno))); + tal_free(uc); + return; + } + + channel_config(peer->ld, &uc->our_config, + &max_to_self_delay, + &min_effective_htlc_capacity); + + /* BOLT #2: + * + * The sender: + * - SHOULD set `minimum_depth` to a number of blocks it considers + * reasonable to avoid double-spending of the funding transaction. + */ + uc->minimum_depth = peer->ld->config.anchor_confirms; + + msg = towire_dual_open_init(NULL, + chainparams, + peer->ld->our_features, + peer->their_features, + &uc->our_config, + max_to_self_delay, + min_effective_htlc_capacity, + pps, &uc->local_basepoints, + &uc->local_funding_pubkey, + uc->minimum_depth, + feerate_min(peer->ld, NULL), + feerate_max(peer->ld, NULL), + feature_negotiated(peer->ld->our_features, + peer->their_features, + OPT_ANCHOR_OUTPUTS), + send_msg); + subd_send_msg(uc->open_daemon, take(msg)); +} +#endif /* EXPERIMENTAL_FEATURES */ diff --git a/lightningd/dual_open_control.h b/lightningd/dual_open_control.h new file mode 100644 index 000000000..072f273d8 --- /dev/null +++ b/lightningd/dual_open_control.h @@ -0,0 +1,11 @@ +#ifndef LIGHTNING_LIGHTNINGD_DUAL_OPEN_CONTROL_H +#define LIGHTNING_LIGHTNINGD_DUAL_OPEN_CONTROL_H + +#include "config.h" + +struct per_peer_state; + +void peer_start_dualopend(struct peer *peer, + struct per_peer_state *pps, + const u8 *send_msg); +#endif /* LIGHTNING_LIGHTNINGD_DUAL_OPEN_CONTROL_H */