diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index fd07de0cb..ba8cbe1b9 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -282,6 +282,13 @@ static void funding_broadcast_failed_or_success(struct channel *channel, } } +static void opening_funder_start_finished(struct subd *openingd, const u8 *resp, + const int *fds, + struct funding_channel *fc) +{ + // todo: this. +} + static void opening_funder_finished(struct subd *openingd, const u8 *resp, const int *fds, struct funding_channel *fc) @@ -889,7 +896,15 @@ static unsigned int openingd_msg(struct subd *openingd, return 3; opening_funder_finished(openingd, msg, fds, uc->fc); return 0; - + case WIRE_OPENING_FUNDER_START_REPLY: + if (!uc->fc) { + log_broken(openingd->log, "Unexpected FUNDER_START_REPLY %s", + tal_hex(tmpctx, msg)); + tal_free(openingd); + return 0; + } + opening_funder_start_finished(openingd, msg, fds, uc->fc); + return 0; case WIRE_OPENING_FUNDER_FAILED: if (!uc->fc) { log_broken(openingd->log, "Unexpected FUNDER_FAILED %s", @@ -913,6 +928,7 @@ static unsigned int openingd_msg(struct subd *openingd, /* We send these! */ case WIRE_OPENING_INIT: case WIRE_OPENING_FUNDER: + case WIRE_OPENING_FUNDER_START: case WIRE_OPENING_GOT_OFFER_REPLY: case WIRE_OPENING_DEV_MEMLEAK: /* Replies never get here */ @@ -989,6 +1005,93 @@ void peer_start_openingd(struct peer *peer, subd_send_msg(uc->openingd, take(msg)); } +static struct command_result *json_fund_channel_start(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct funding_channel * fc = tal(cmd, struct funding_channel); + struct node_id *id; + struct peer *peer; + struct channel *channel; + bool *announce_channel; + u32 *feerate_per_kw; + + u8 *msg = NULL; + struct amount_sat max_funding_satoshi, *amount; + + max_funding_satoshi = get_chainparams(cmd->ld)->max_funding; + fc->cmd = cmd; + fc->uc = NULL; + if (!param(fc->cmd, buffer, params, + p_req("id", param_node_id, &id), + p_req("satoshi", param_sat, &amount), + p_opt("feerate", param_feerate, &feerate_per_kw), + p_opt_def("announce", param_bool, &announce_channel, true), + NULL)) + return command_param_failed(); + + if (amount_sat_greater(*amount, max_funding_satoshi)) + return command_fail(cmd, FUND_MAX_EXCEEDED, + "Amount exceeded %s", + type_to_string(tmpctx, struct amount_sat, + &max_funding_satoshi)); + + if (!feerate_per_kw) { + feerate_per_kw = tal(cmd, u32); + *feerate_per_kw = opening_feerate(cmd->ld->topology); + if (!*feerate_per_kw) { + return command_fail(cmd, LIGHTNINGD, + "Cannot estimate fees"); + } + } + + if (*feerate_per_kw < feerate_floor()) { + return command_fail(cmd, LIGHTNINGD, + "Feerate below feerate floor"); + } + + peer = peer_by_id(cmd->ld, id); + if (!peer) { + return command_fail(cmd, LIGHTNINGD, "Unknown peer"); + } + + channel = peer_active_channel(peer); + if (channel) { + return command_fail(cmd, LIGHTNINGD, "Peer already %s", + channel_state_name(channel)); + } + + if (!peer->uncommitted_channel) { + return command_fail(cmd, LIGHTNINGD, "Peer not connected"); + } + + if (peer->uncommitted_channel->fc) { + return command_fail(cmd, LIGHTNINGD, "Already funding channel"); + } + + fc->push = AMOUNT_MSAT(0); + fc->channel_flags = OUR_CHANNEL_FLAGS; + if (!*announce_channel) { + fc->channel_flags &= ~CHANNEL_FLAGS_ANNOUNCE_CHANNEL; + log_info(peer->ld->log, "Will open private channel with node %s", + type_to_string(fc, struct node_id, id)); + } + + assert(!amount_sat_greater(*amount, max_funding_satoshi)); + peer->uncommitted_channel->fc = tal_steal(peer->uncommitted_channel, fc); + fc->uc = peer->uncommitted_channel; + + msg = towire_opening_funder_start(NULL, + *amount, + fc->push, + *feerate_per_kw, + fc->channel_flags); + + subd_send_msg(peer->uncommitted_channel->openingd, take(msg)); + return command_still_pending(cmd); +} + /** * json_fund_channel - Entrypoint for funding a channel */ @@ -1106,6 +1209,15 @@ static const struct json_command fund_channel_command = { }; AUTODATA(json_command, &fund_channel_command); +static const struct json_command fund_channel_start_command = { + "fundchannel_start", + "channels", + json_fund_channel_start, + "Start fund channel with {id} using {amount} satoshis. " + "Returns a bech32 address to use as an output for a funding transaction." +}; +AUTODATA(json_command, &fund_channel_start_command); + #if DEVELOPER /* Indented to avoid include ordering check */ #include diff --git a/openingd/opening_wire.csv b/openingd/opening_wire.csv index 6f0eaf685..2e4f778d8 100644 --- a/openingd/opening_wire.csv +++ b/openingd/opening_wire.csv @@ -77,6 +77,19 @@ opening_funder_reply,,our_channel_reserve_satoshis,struct amount_sat opening_funder_reply,,shutdown_len,u16 opening_funder_reply,,shutdown_scriptpubkey,shutdown_len*u8 +# master->openingd: start channel establishment for a funding +# tx that will be paid for by an external wallet +opening_funder_start,6002 +opening_funder_start,,funding_satoshis,struct amount_sat +opening_funder_start,,push_msat,struct amount_msat +opening_funder_start,,feerate_per_kw,u32 +opening_funder_start,,channel_flags,u8 + +# openingd->master: send back output script for 2-of-2 funding output +opening_funder_start_reply,6102 +opening_funder_start_reply,,script_len,u8 +opening_funder_start_reply,,scriptpubkey,script_len*u8 + # Openingd->master: we failed to negotiation channel opening_funder_failed,6004 opening_funder_failed,,reason,wirestring diff --git a/openingd/openingd.c b/openingd/openingd.c index a1beb6b67..1820470e5 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -438,6 +438,14 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state, } } +/* We start the 'fund a channel' negotation with the supplied peer, but + * stop when we get to the part where we need the funding txid */ +static u8 *funder_channel_start(struct state *state, + u8 channel_flags) +{ + return towire_opening_funder_start_reply(state, NULL); +} + /*~ OK, let's fund a channel! Returns the reply for lightningd on success, * or NULL if something goes wrong. */ static u8 *funder_channel(struct state *state, @@ -1425,7 +1433,17 @@ static u8 *handle_master_in(struct state *state) change_keyindex, channel_flags, take(utxos), &bip32_base); return msg; - + case WIRE_OPENING_FUNDER_START: + if (!fromwire_opening_funder_start(msg, &state->funding, + &state->push_msat, + &state->feerate_per_kw, + &channel_flags)) + master_badmsg(WIRE_OPENING_FUNDER_START, msg); + msg = funder_channel_start(state, channel_flags); + + /* We want to keep openingd alive, since we're not done yet */ + wire_sync_write(REQ_FD, take(msg)); + return NULL; case WIRE_OPENING_DEV_MEMLEAK: #if DEVELOPER handle_dev_memleak(state, msg); @@ -1434,6 +1452,7 @@ static u8 *handle_master_in(struct state *state) case WIRE_OPENING_DEV_MEMLEAK_REPLY: case WIRE_OPENING_INIT: case WIRE_OPENING_FUNDER_REPLY: + case WIRE_OPENING_FUNDER_START_REPLY: case WIRE_OPENING_FUNDEE: case WIRE_OPENING_FUNDER_FAILED: case WIRE_OPENING_GOT_OFFER: