API: `fundchannel_cancel` can cancel fundchannel process before funding broadcast

This commit is contained in:
trueptolemy 2019-08-24 05:34:52 +08:00 committed by neil saitug
parent 1e5b619832
commit 6c708b5854
7 changed files with 215 additions and 34 deletions

View File

@ -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

1 #include <common/cryptomsg.h>
198
199
200
201
202
203
204
205
206
207

View File

@ -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)
{
@ -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);
@ -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);

View File

@ -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>
@ -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>
@ -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);
@ -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:
@ -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;
}
@ -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);
}

View File

@ -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 */

View File

@ -295,7 +295,8 @@ static void funding_success(struct channel *channel)
/* Well, those cancels didn't work! */
for (size_t i = 0; i < tal_count(fc->cancels); i++)
was_pending(command_fail(fc->cancels[i], LIGHTNINGD,
"Funding succeeded before cancel"));
"Funding succeeded before cancel. "
"Try fundchannel_cancel again."));
response = json_stream_success(cmd);
json_add_string(response, "channel_id",
@ -1189,7 +1190,7 @@ static struct command_result *json_fund_channel_complete(struct command *cmd,
}
/**
* json_fund_channel_cancel - Entrypoint for cancelling an in flight channel-funding
* json_fund_channel_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,
@ -1199,10 +1200,12 @@ static struct command_result *json_fund_channel_cancel(struct command *cmd,
struct node_id *id;
struct peer *peer;
const jsmntok_t *cidtok;
u8 *msg;
if (!param(cmd, buffer, params,
p_req("id", param_node_id, &id),
p_opt("channel_id", param_tok, &cidtok),
NULL))
return command_param_failed();
@ -1211,40 +1214,18 @@ static struct command_result *json_fund_channel_cancel(struct command *cmd,
return command_fail(cmd, LIGHTNINGD, "Unknown peer");
}
if (!peer->uncommitted_channel) {
return command_fail(cmd, LIGHTNINGD, "Peer not connected");
if (peer->uncommitted_channel) {
if(!peer->uncommitted_channel->fc || !peer->uncommitted_channel->fc->inflight)
return command_fail(cmd, LIGHTNINGD, "No channel funding in progress.");
/* Make sure this gets notified if we succeed or cancel */
tal_arr_expand(&peer->uncommitted_channel->fc->cancels, cmd);
msg = towire_opening_funder_cancel(NULL);
subd_send_msg(peer->uncommitted_channel->openingd, take(msg));
return command_still_pending(cmd);
}
if (!peer->uncommitted_channel->fc || !peer->uncommitted_channel->fc->inflight)
return command_fail(cmd, LIGHTNINGD, "No channel funding in progress.");
/**
* there's a question of 'state machinery' here. as is, we're not checking
* to see if you've already called `complete` -- we expect you
* the caller to EITHER pick 'complete' or 'cancel'.
* but if for some reason you've decided to test your luck, how much
* 'handling' can we do for that case? the easiest thing to do is to
* say "sorry you've already called complete", we can't cancel this.
*
* there's also the state you might end up in where you've called
* complete (and it's completed and been passed off to channeld) but
* you've decided (for whatever reason) not to broadcast the transaction
* so your channels have ended up in this 'waiting' state. neither of us
* are actually out any amount of cash, but it'd be nice if there's a way
* to signal to c-lightning (+ your peer) that this channel is dead on arrival.
* ... but also if you then broadcast this tx you'd be in trouble cuz we're
* both going to forget about it. the meta question here is how 'undoable'
* should we make any of this. how much tools do we give you, reader?
*
* for now, let's settle for the EITHER / OR case and disregard the larger
* question about 'how long cancelable'.
*/
/* Make sure this gets notified if we succeed or cancel */
tal_arr_expand(&peer->uncommitted_channel->fc->cancels, cmd);
msg = towire_opening_funder_cancel(NULL);
subd_send_msg(peer->uncommitted_channel->openingd, take(msg));
return command_still_pending(cmd);
return cancel_channel_before_broadcast(cmd, buffer, peer, cidtok);
}
/**

View File

@ -2847,6 +2847,23 @@ void wallet_transaction_annotate(struct wallet *w,
db_exec_prepared_v2(take(stmt));
}
bool wallet_transaction_type(struct wallet *w, const struct bitcoin_txid *txid,
enum wallet_tx_type *type)
{
struct db_stmt *stmt = db_prepare_v2(w->db, SQL("SELECT type FROM transactions WHERE id=?"));
db_bind_sha256(stmt, 0, &txid->shad.sha);
db_query_prepared(stmt);
if (!db_step(stmt)) {
tal_free(stmt);
return false;
}
*type = db_column_int(stmt, 0);
tal_free(stmt);
return true;
}
u32 wallet_transaction_height(struct wallet *w, const struct bitcoin_txid *txid)
{
u32 blockheight;

View File

@ -1066,6 +1066,16 @@ void wallet_transaction_annotate(struct wallet *w,
const struct bitcoin_txid *txid,
enum wallet_tx_type type, u64 channel_id);
/**
* Get the type of a transaction we are watching by its
* txid.
*
* Returns false if the transaction was not stored in DB.
* Returns true if the transaction exists and sets the `type` parameter.
*/
bool wallet_transaction_type(struct wallet *w, const struct bitcoin_txid *txid,
enum wallet_tx_type *type);
/**
* Get the confirmation height of a transaction we are watching by its
* txid. Returns 0 if the transaction was not part of any block.