API: `fundchannel_cancel` can cancel fundchannel process before funding broadcast
This commit is contained in:
parent
1e5b619832
commit
6c708b5854
|
@ -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
|
||||
|
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue