Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- JSON API: `txprepare` now uses `outputs` as parameter other than `destination` and `satoshi`
- Build: Now requires [`gettext`](https://www.gnu.org/software/gettext/)
- JSON API: `fundchannel_cancel` is extended to work before funding broadcast.

### Deprecated

Expand Down
7 changes: 7 additions & 0 deletions channeld/channel_wire.csv
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,10 @@ msgdata,channel_specific_feerates,feerate_ppm,u32,
msgtype,channel_got_announcement,1017
msgdata,channel_got_announcement,remote_ann_node_sig,secp256k1_ecdsa_signature,
msgdata,channel_got_announcement,remote_ann_bitcoin_sig,secp256k1_ecdsa_signature,

# Ask channeld to send a error message. Used in forgetting channel case.
msgtype,channel_send_error,1008
msgdata,channel_send_error,reason,wirestring,

# Tell master channeld has sent the error message.
msgtype,channel_send_error_reply,1108
18 changes: 18 additions & 0 deletions channeld/channeld.c
Original file line number Diff line number Diff line change
Expand Up @@ -2796,6 +2796,20 @@ static void handle_shutdown_cmd(struct peer *peer, const u8 *inmsg)
start_commit_timer(peer);
}

static void handle_send_error(struct peer *peer, const u8 *msg)
{
char *reason;
if (!fromwire_channel_send_error(msg, msg, &reason))
master_badmsg(WIRE_CHANNEL_SEND_ERROR, msg);
status_debug("Send error reason: %s", reason);
sync_crypto_write(peer->pps,
take(towire_errorfmt(NULL, &peer->channel_id,
"%s", reason)));

wire_sync_write(MASTER_FD,
take(towire_channel_send_error_reply(NULL)));
}

#if DEVELOPER
static void handle_dev_reenable_commit(struct peer *peer)
{
Expand Down Expand Up @@ -2849,6 +2863,9 @@ static void req_in(struct peer *peer, const u8 *msg)
case WIRE_CHANNEL_SEND_SHUTDOWN:
handle_shutdown_cmd(peer, msg);
return;
case WIRE_CHANNEL_SEND_ERROR:
handle_send_error(peer, msg);
return;
#if DEVELOPER
case WIRE_CHANNEL_DEV_REENABLE_COMMIT:
handle_dev_reenable_commit(peer);
Expand All @@ -2875,6 +2892,7 @@ static void req_in(struct peer *peer, const u8 *msg)
case WIRE_CHANNEL_DEV_REENABLE_COMMIT_REPLY:
case WIRE_CHANNEL_FAIL_FALLEN_BEHIND:
case WIRE_CHANNEL_DEV_MEMLEAK_REPLY:
case WIRE_CHANNEL_SEND_ERROR_REPLY:
break;
}
master_badmsg(-1, msg);
Expand Down
13 changes: 10 additions & 3 deletions doc/lightning-fundchannel_cancel.7
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@ lightning-fundchannel_cancel - Command for completing channel establishment

.SH DESCRIPTION

\fBfundchannel_cancel\fR is a lower level RPC command\. It allows a user to
cancel an initiated channel establishment with a connected peer\.
\fBfundchannel_cancel\fR is a lower level RPC command\. It allows channel funder
to cancel a channel before funding broadcast with a connected peer\.

\fIid\fR is the node id of the remote peer with which to cancel\.

\fIid\fR is the node id of the remote peer with which to cancel the
Note that the funding transaction MUST NOT be broadcast before
\fBfundchannel_cancel\fR\. Broadcasting transaction before \fBfundchannel_cancel\fR
WILL lead to unrecoverable loss of funds\.

If \fBfundchannel_cancel\fR is called after \fBfundchannel_complete\fR, the remote
peer may disconnect when command succeeds\. In this case, user need to connect
to remote peer again before opening channel\.

.SH RETURN VALUE

Expand Down
14 changes: 11 additions & 3 deletions doc/lightning-fundchannel_cancel.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@ SYNOPSIS
DESCRIPTION
-----------

`fundchannel_cancel` is a lower level RPC command. It allows a user to
cancel an initiated channel establishment with a connected peer.
`fundchannel_cancel` is a lower level RPC command. It allows channel funder
to cancel a channel before funding broadcast with a connected peer.

*id* is the node id of the remote peer with which to cancel the
*id* is the node id of the remote peer with which to cancel.

Note that the funding transaction MUST NOT be broadcast before
`fundchannel_cancel`. Broadcasting transaction before `fundchannel_cancel`
WILL lead to unrecoverable loss of funds.

If `fundchannel_cancel` is called after `fundchannel_complete`, the remote
peer may disconnect when command succeeds. In this case, user need to connect
to remote peer again before opening channel.

RETURN VALUE
------------
Expand Down
36 changes: 35 additions & 1 deletion lightningd/channel.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include <bitcoin/script.h>
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
#include <ccan/tal/str/str.h>
#include <common/json_command.h>
#include <common/jsonrpc_errors.h>
#include <common/wire_error.h>
#include <connectd/gen_connect_wire.h>
#include <errno.h>
Expand Down Expand Up @@ -97,6 +99,10 @@ static void destroy_channel(struct channel *channel)
channel_state_name(channel),
htlc_state_name(hin->hstate));

for (size_t i = 0; i < tal_count(channel->forgets); i++)
was_pending(command_fail(channel->forgets[i], LIGHTNINGD,
"Channel structure was freed!"));

/* Free any old owner still hanging around. */
channel_set_owner(channel, NULL);

Expand Down Expand Up @@ -250,6 +256,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid,
channel->remote_upfront_shutdown_script
= tal_steal(channel, remote_upfront_shutdown_script);
channel->option_static_remotekey = option_static_remotekey;
channel->forgets = tal_arr(channel, struct command *, 0);

list_add_tail(&peer->channels, &channel->list);
tal_add_destructor(channel, destroy_channel);
Expand Down Expand Up @@ -362,7 +369,7 @@ void channel_fail_permanent(struct channel *channel, const char *fmt, ...)
struct channel_id cid;

va_start(ap, fmt);
why = tal_vfmt(channel, fmt, ap);
why = tal_vfmt(tmpctx, fmt, ap);
va_end(ap);

log_unusual(channel->log, "Peer permanent failure in %s: %s",
Expand All @@ -386,6 +393,33 @@ void channel_fail_permanent(struct channel *channel, const char *fmt, ...)
tal_free(why);
}

void channel_fail_forget(struct channel *channel, const char *fmt, ...)
{
va_list ap;
char *why;
struct channel_id cid;

assert(channel->funder == REMOTE &&
channel->state == CHANNELD_AWAITING_LOCKIN);
va_start(ap, fmt);
why = tal_vfmt(tmpctx, fmt, ap);
va_end(ap);

log_unusual(channel->log, "Peer permanent failure in %s: %s, "
"forget channel",
channel_state_name(channel), why);

if (!channel->error) {
derive_channel_id(&cid,
&channel->funding_txid,
channel->funding_outnum);
channel->error = towire_errorfmt(channel, &cid, "%s", why);
}

delete_channel(channel);
tal_free(why);
}

void channel_internal_error(struct channel *channel, const char *fmt, ...)
{
va_list ap;
Expand Down
6 changes: 6 additions & 0 deletions lightningd/channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ struct channel {

/* Was this negotiated with `option_static_remotekey? */
bool option_static_remotekey;

/* Any commands trying to forget us. */
struct command **forgets;
};

struct channel *new_channel(struct peer *peer, u64 dbid,
Expand Down Expand Up @@ -187,6 +190,9 @@ PRINTF_FMT(2,3) void channel_fail_reconnect_later(struct channel *channel,

/* Channel has failed, give up on it. */
void channel_fail_permanent(struct channel *channel, const char *fmt, ...);
/* Forget the channel. This is only used for the case when we "receive" error
* during CHANNELD_AWAITING_LOCKIN if we are "fundee". */
void channel_fail_forget(struct channel *channel, const char *fmt, ...);
/* Permanent error, but due to internal problems, not peer. */
void channel_internal_error(struct channel *channel, const char *fmt, ...);

Expand Down
141 changes: 141 additions & 0 deletions lightningd/channel_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
#include <ccan/cast/cast.h>
#include <channeld/gen_channel_wire.h>
#include <common/features.h>
#include <common/json_command.h>
#include <common/jsonrpc_errors.h>
#include <common/memleak.h>
#include <common/per_peer_state.h>
#include <common/timeout.h>
#include <common/utils.h>
#include <common/wallet_tx.h>
#include <common/wire_error.h>
#include <errno.h>
#include <gossipd/gossip_constants.h>
Expand All @@ -15,6 +18,7 @@
#include <lightningd/channel_control.h>
#include <lightningd/closing_control.h>
#include <lightningd/hsm_control.h>
#include <lightningd/jsonrpc.h>
#include <lightningd/lightningd.h>
#include <lightningd/log.h>
#include <lightningd/peer_control.h>
Expand Down Expand Up @@ -230,6 +234,33 @@ static void peer_start_closingd_after_shutdown(struct channel *channel,
channel_set_state(channel, CHANNELD_SHUTTING_DOWN, CLOSINGD_SIGEXCHANGE);
}

static void handle_error_channel(struct channel *channel,
const u8 *msg)
{
struct command **forgets = tal_steal(tmpctx, channel->forgets);
channel->forgets = tal_arr(channel, struct command *, 0);

if (!fromwire_channel_send_error_reply(msg)) {
channel_internal_error(channel, "bad send_error_reply: %s",
tal_hex(tmpctx, msg));
return;
}

/* Forget the channel. */
delete_channel(channel);

for (size_t i = 0; i < tal_count(forgets); i++) {
assert(!forgets[i]->json_stream);

struct json_stream *response;
response = json_stream_success(forgets[i]);
json_add_string(response, "cancelled", "Channel open canceled by RPC(after fundchannel_complete)");
was_pending(command_success(forgets[i], response));
}

tal_free(forgets);
}

static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds)
{
enum channel_wire_type t = fromwire_peektype(msg);
Expand Down Expand Up @@ -262,6 +293,9 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds)
case WIRE_CHANNEL_FAIL_FALLEN_BEHIND:
channel_fail_fallen_behind(sd->channel, msg);
break;
case WIRE_CHANNEL_SEND_ERROR_REPLY:
handle_error_channel(sd->channel, msg);
break;

/* And we never get these from channeld. */
case WIRE_CHANNEL_INIT:
Expand All @@ -281,6 +315,7 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds)
case WIRE_CHANNEL_OFFER_HTLC_REPLY:
case WIRE_CHANNEL_DEV_REENABLE_COMMIT_REPLY:
case WIRE_CHANNEL_DEV_MEMLEAK_REPLY:
case WIRE_CHANNEL_SEND_ERROR:
break;
}

Expand Down Expand Up @@ -578,3 +613,109 @@ void channel_notify_new_block(struct lightningd *ld,

tal_free(to_forget);
}

static void process_check_funding_broadcast(struct bitcoind *bitcoind UNUSED,
const struct bitcoin_tx_output *txout,
void *arg)
{
struct channel *cancel = arg;

if (txout != NULL) {
for (size_t i = 0; i < tal_count(cancel->forgets); i++)
was_pending(command_fail(cancel->forgets[i], LIGHTNINGD,
"The funding transaction has been broadcast, "
"please consider `close` or `dev-fail`! "));
tal_free(cancel->forgets);
cancel->forgets = tal_arr(cancel, struct command *, 0);
return;
}

const char *error_reason = "Cancel channel by our RPC "
"command before funding "
"transaction broadcast.";
/* Set error so we don't try to reconnect. */
cancel->error = towire_errorfmt(cancel, NULL, "%s", error_reason);

subd_send_msg(cancel->owner,
take(towire_channel_send_error(NULL, error_reason)));
}

struct command_result *cancel_channel_before_broadcast(struct command *cmd,
const char *buffer,
struct peer *peer,
const jsmntok_t *cidtok)
{
struct channel *cancel_channel, *channel;

cancel_channel = NULL;
if (!cidtok) {
list_for_each(&peer->channels, channel, list) {
if (cancel_channel) {
return command_fail(cmd, LIGHTNINGD,
"Multiple channels:"
" please specify channel_id");
}
cancel_channel = channel;
}
if (!cancel_channel)
return command_fail(cmd, LIGHTNINGD,
"No channels matching that peer_id");
} else {
struct channel_id channel_cid;
struct channel_id cid;
if (!json_tok_channel_id(buffer, cidtok, &cid))
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Invalid channel_id parameter.");

list_for_each(&peer->channels, channel, list) {
if (!channel)
return command_fail(cmd, LIGHTNINGD,
"No channels matching "
"that peer_id");
derive_channel_id(&channel_cid,
&channel->funding_txid,
channel->funding_outnum);
if (channel_id_eq(&channel_cid, &cid)) {
cancel_channel = channel;
break;
}
}
if (!cancel_channel)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Channel ID not found: '%.*s'",
cidtok->end - cidtok->start,
buffer + cidtok->start);
}

/* Check if we broadcast the transaction. (We store the transaction type into DB
* before broadcast). */
enum wallet_tx_type type;
if(wallet_transaction_type(cmd->ld->wallet,
&cancel_channel->funding_txid,
&type))
return command_fail(cmd, LIGHTNINGD,
"Has the funding transaction been broadcast? "
"Please use `close` or `dev-fail` instead.");

if (channel_has_htlc_out(cancel_channel) ||
channel_has_htlc_in(cancel_channel)) {
return command_fail(cmd, LIGHTNINGD,
"This channel has HTLCs attached and it is "
"not safe to cancel. Has the funding transaction "
"been broadcast? Please use `close` or `dev-fail` "
"instead.");
}

tal_arr_expand(&cancel_channel->forgets, cmd);

/* Check if the transaction is onchain. */
/* Note: The above check and this check can't completely ensure that
* the funding transaction isn't broadcast. We can't know if the funding
* is broadcast by external wallet and the transaction hasn't been onchain. */
bitcoind_gettxout(cmd->ld->topology->bitcoind,
&cancel_channel->funding_txid,
cancel_channel->funding_outnum,
process_check_funding_broadcast,
cancel_channel);
return command_still_pending(cmd);
}
7 changes: 7 additions & 0 deletions lightningd/channel_control.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,11 @@ bool channel_tell_depth(struct lightningd *ld,
void channel_notify_new_block(struct lightningd *ld,
u32 block_height);

/* Cancel the channel after `fundchannel_complete` succeeds
* but before funding broadcasts. */
struct command_result *cancel_channel_before_broadcast(struct command *cmd,
const char *buffer,
struct peer *peer,
const jsmntok_t *cidtok);

#endif /* LIGHTNING_LIGHTNINGD_CHANNEL_CONTROL_H */
Loading