Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fundchannel complete psbt not txid #4428

12 changes: 6 additions & 6 deletions doc/lightning-fundchannel_complete.7

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions doc/lightning-fundchannel_complete.7.md
Expand Up @@ -4,7 +4,7 @@ lightning-fundchannel\_complete -- Command for completing channel establishment
SYNOPSIS
--------

**fundchannel\_complete** *id* *txid* *txout*
**fundchannel\_complete** *id* *psbt*

DESCRIPTION
-----------
Expand All @@ -14,9 +14,8 @@ complete an initiated channel establishment with a connected peer.

*id* is the node id of the remote peer.

*txid* is the hex string of the funding transaction id.

*txout* is the integer outpoint of the funding output for this channel.
*psbt* is the transaction to use for funding (does not need to be
signed but must be otherwise complete).

Note that the funding transaction MUST NOT be broadcast until after
channel establishment has been successfully completed, as the commitment
Expand All @@ -37,6 +36,7 @@ with `code` being one of the following:
- -1: Catchall nonspecific error.
- 305: Peer is not connected.
- 306: Unknown peer id.
- 309: PSBT does not have a unique, correct output to fund the channel.

AUTHOR
------
Expand Down
3 changes: 3 additions & 0 deletions lightningd/opening_common.h
Expand Up @@ -78,6 +78,9 @@ struct funding_channel {
/* Channel, subsequent owner of us */
struct uncommitted_channel *uc;

/* The scriptpubkey to pay (once started) */
u8 *funding_scriptpubkey;

/* Whether or not this is in the middle of getting funded */
bool inflight;

Expand Down
154 changes: 108 additions & 46 deletions lightningd/opening_control.c
@@ -1,6 +1,8 @@
#include "bitcoin/feerate.h"
#include <bitcoin/privkey.h>
#include <bitcoin/psbt.h>
#include <bitcoin/script.h>
#include <ccan/mem/mem.h>
#include <ccan/tal/str/str.h>
#include <common/addr.h>
#include <common/channel_config.h>
Expand Down Expand Up @@ -266,9 +268,7 @@ static void funding_success(struct channel *channel)
was_pending(command_success(cmd, response));
}

static void funding_started_success(struct funding_channel *fc,
u8 *scriptPubkey,
bool supports_shutdown)
static void funding_started_success(struct funding_channel *fc)
{
struct json_stream *response;
struct command *cmd = fc->cmd;
Expand All @@ -277,10 +277,11 @@ static void funding_started_success(struct funding_channel *fc,
response = json_stream_success(cmd);
out = encode_scriptpubkey_to_addr(cmd,
chainparams,
scriptPubkey);
fc->funding_scriptpubkey);
if (out) {
json_add_string(response, "funding_address", out);
json_add_hex_talarr(response, "scriptpubkey", scriptPubkey);
json_add_hex_talarr(response, "scriptpubkey",
fc->funding_scriptpubkey);
if (fc->our_upfront_shutdown_script)
json_add_hex_talarr(response, "close_to", fc->our_upfront_shutdown_script);
}
Expand Down Expand Up @@ -314,7 +315,9 @@ static void opening_funder_start_replied(struct subd *openingd, const u8 *resp,
fc->our_upfront_shutdown_script =
tal_free(fc->our_upfront_shutdown_script);

funding_started_success(fc, funding_scriptPubkey, supports_shutdown_script);
/* Save this so we can indentify output for scriptpubkey */
fc->funding_scriptpubkey = tal_steal(fc, funding_scriptPubkey);
funding_started_success(fc);

/* Mark that we're in-flight */
fc->inflight = true;
Expand Down Expand Up @@ -946,32 +949,48 @@ void peer_start_openingd(struct peer *peer,
subd_send_msg(uc->open_daemon, take(msg));
}

static struct command_result *json_fund_channel_complete(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
static struct command_result *json_fundchannel_complete(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
u8 *msg;
struct node_id *id;
struct bitcoin_txid *funding_txid;
struct peer *peer;
struct channel *channel;
u32 *funding_txout_num;
u16 funding_txout;

if (!param(cmd, buffer, params,
p_req("id", param_node_id, &id),
p_req("txid", param_txid, &funding_txid),
p_req("txout", param_number, &funding_txout_num),
NULL))
return command_param_failed();

if (*funding_txout_num > UINT16_MAX)
return command_fail(cmd, LIGHTNINGD,
"Invalid parameter: funding tx vout too large %u",
*funding_txout_num);
struct wally_psbt *funding_psbt;
u32 *funding_txout_num = NULL;
struct funding_channel *fc;
bool old_api;

/* params is NULL for initial parameter desc generation! */
if (params /* FIXME: && deprecated_apis */) {
/* We used to have a three-arg version. */
if (params->type == JSMN_ARRAY)
old_api = (params->size == 3);
else
old_api = (json_get_member(buffer, params, "txid")
!= NULL);
if (old_api) {
if (!param(cmd, buffer, params,
p_req("id", param_node_id, &id),
p_req("txid", param_txid, &funding_txid),
p_req("txout", param_number, &funding_txout_num),
NULL))
return command_param_failed();
}
} else
old_api = false;

if (!old_api) {
if (!param(cmd, buffer, params,
p_req("id", param_node_id, &id),
p_req("psbt", param_psbt, &funding_psbt),
NULL))
return command_param_failed();
}

funding_txout = *funding_txout_num;
peer = peer_by_id(cmd->ld, id);
if (!peer) {
return command_fail(cmd, FUNDING_UNKNOWN_PEER, "Unknown peer");
Expand All @@ -991,22 +1010,64 @@ static struct command_result *json_fund_channel_complete(struct command *cmd,
if (peer->uncommitted_channel->fc->cmd)
return command_fail(cmd, LIGHTNINGD, "Channel funding in progress.");

fc = peer->uncommitted_channel->fc;

if (!old_api) {
/* Figure out the correct output, and perform sanity checks. */
for (size_t i = 0; i < funding_psbt->tx->num_outputs; i++) {
if (memeq(funding_psbt->tx->outputs[i].script,
funding_psbt->tx->outputs[i].script_len,
fc->funding_scriptpubkey,
tal_bytelen(fc->funding_scriptpubkey))) {
if (funding_txout_num)
return command_fail(cmd, FUNDING_PSBT_INVALID,
"Two outputs to open channel");
funding_txout_num = tal(cmd, u32);
*funding_txout_num = i;
}
}
if (!funding_txout_num)
return command_fail(cmd, FUNDING_PSBT_INVALID,
"No output to open channel");

if (!amount_sat_eq(amount_sat(funding_psbt->tx->outputs
[*funding_txout_num].satoshi),
fc->funding))
return command_fail(cmd, FUNDING_PSBT_INVALID,
"Output to open channel is %"PRIu64"sat,"
" should be %s",
funding_psbt->tx->outputs
[*funding_txout_num].satoshi,
type_to_string(tmpctx, struct amount_sat,
&fc->funding));

funding_txid = tal(cmd, struct bitcoin_txid);
psbt_txid(NULL, funding_psbt, funding_txid, NULL);
}

/* Fun fact: our wire protocol only allows 16 bits for outnum.
* That is reflected in our encoding scheme for short_channel_id. */
if (*funding_txout_num > UINT16_MAX)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Invalid parameter: funding tx vout too large %u",
*funding_txout_num);

/* Set the cmd to this new cmd */
peer->uncommitted_channel->fc->cmd = cmd;
msg = towire_openingd_funder_complete(NULL,
funding_txid,
funding_txout);
*funding_txout_num);
subd_send_msg(peer->uncommitted_channel->open_daemon, take(msg));
return command_still_pending(cmd);
}

/**
* json_fund_channel_cancel - Entrypoint for cancelling a channel which funding isn't broadcast
* json_fundchannel_cancel - Entrypoint for cancelling a channel which funding isn't broadcast
*/
static struct command_result *json_fund_channel_cancel(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
static struct command_result *json_fundchannel_cancel(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{

struct node_id *id;
Expand Down Expand Up @@ -1040,12 +1101,12 @@ static struct command_result *json_fund_channel_cancel(struct command *cmd,
}

/**
* json_fund_channel_start - Entrypoint for funding a channel
* json_fundchannel_start - Entrypoint for funding a channel
*/
static struct command_result *json_fund_channel_start(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
static struct command_result *json_fundchannel_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;
Expand All @@ -1062,6 +1123,7 @@ static struct command_result *json_fund_channel_start(struct command *cmd,
fc->cancels = tal_arr(fc, struct command *, 0);
fc->uc = NULL;
fc->inflight = false;
fc->funding_scriptpubkey = NULL;

if (!param(fc->cmd, buffer, params,
p_req("id", param_node_id, &id),
Expand Down Expand Up @@ -1168,31 +1230,31 @@ static struct command_result *json_fund_channel_start(struct command *cmd,
return command_still_pending(cmd);
}

static const struct json_command fund_channel_start_command = {
static const struct json_command fundchannel_start_command = {
"fundchannel_start",
"channels",
json_fund_channel_start,
json_fundchannel_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);
AUTODATA(json_command, &fundchannel_start_command);

static const struct json_command fund_channel_cancel_command = {
static const struct json_command fundchannel_cancel_command = {
"fundchannel_cancel",
"channels",
json_fund_channel_cancel,
json_fundchannel_cancel,
"Cancel inflight channel establishment with peer {id}."
};
AUTODATA(json_command, &fund_channel_cancel_command);
AUTODATA(json_command, &fundchannel_cancel_command);

static const struct json_command fund_channel_complete_command = {
static const struct json_command fundchannel_complete_command = {
"fundchannel_complete",
"channels",
json_fund_channel_complete,
json_fundchannel_complete,
"Complete channel establishment with peer {id} for funding transaction"
"with {txid}. Returns true on success, false otherwise."
"with {psbt}. Returns true on success, false otherwise."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this will actually make us incompatible with LN Pool? surely not... as the pool probably produces PSBTs for peers to sign for the collectively built opening tx?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can always re-add a txid arg, but I really think that it's a great sanity-check!

};
AUTODATA(json_command, &fund_channel_complete_command);
AUTODATA(json_command, &fundchannel_complete_command);

struct subd *peer_get_owning_subd(struct peer *peer)
{
Expand Down