Browse Source

opening: add fundchannel_cancel command

Provide the option to cancel a funding-opening with a peer.
Must either call `fundchannel_cancel` or `fundchannel_continue`
pull/2938/head
lisa neigut 6 years ago
committed by Rusty Russell
parent
commit
5aad642c59
  1. 9
      contrib/pylightning/lightning/lightning.py
  2. 71
      lightningd/opening_control.c
  3. 3
      openingd/opening_wire.csv
  4. 10
      openingd/openingd.c
  5. 8
      tests/test_connection.py

9
contrib/pylightning/lightning/lightning.py

@ -499,6 +499,15 @@ class LightningRpc(UnixDomainSocketRpc):
}
return self.call("fundchannel_start", payload)
def fundchannel_cancel(self, node_id):
"""
Cancel a 'started' fundchannel with node {id}.
"""
payload = {
"id": node_id,
}
return self.call("fundchannel_cancel", payload)
def fundchannel_continue(self, node_id, funding_txid, funding_txout):
"""
Complete channel establishment with {id}, using {funding_txid} at {funding_txout}

71
lightningd/opening_control.c

@ -1021,6 +1021,7 @@ static unsigned int openingd_msg(struct subd *openingd,
case WIRE_OPENING_FUNDER:
case WIRE_OPENING_FUNDER_START:
case WIRE_OPENING_FUNDER_CONTINUE:
case WIRE_OPENING_FUNDER_CANCEL:
case WIRE_OPENING_GOT_OFFER_REPLY:
case WIRE_OPENING_DEV_MEMLEAK:
/* Replies never get here */
@ -1148,6 +1149,68 @@ static struct command_result *json_fund_channel_continue(struct command *cmd,
return command_still_pending(cmd);
}
/**
* json_fund_channel_cancel - Entrypoint for cancelling an in flight channel-funding
*/
static struct command_result *json_fund_channel_cancel(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct node_id *id;
struct peer *peer;
u8 *msg;
if (!param(cmd, buffer, params,
p_req("id", param_node_id, &id),
NULL))
return command_param_failed();
peer = peer_by_id(cmd->ld, id);
if (!peer) {
return command_fail(cmd, LIGHTNINGD, "Unknown peer");
}
if (!peer->uncommitted_channel) {
return command_fail(cmd, LIGHTNINGD, "Peer not connected");
}
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 `continue` -- we expect you
* the caller to EITHER pick 'continue' 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 continue", we can't cancel this.
*
* there's also the state you might end up in where you've called
* continue (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'.
*/
/* Update the cmd to the new cmd */
peer->uncommitted_channel->fc->cmd = cmd;
msg = towire_opening_funder_cancel(NULL);
subd_send_msg(peer->uncommitted_channel->openingd, take(msg));
return command_still_pending(cmd);
}
/**
* json_fund_channel_start - Entrypoint for funding an externally funded channel
*/
static struct command_result *json_fund_channel_start(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
@ -1367,6 +1430,14 @@ static const struct json_command fund_channel_start_command = {
};
AUTODATA(json_command, &fund_channel_start_command);
static const struct json_command fund_channel_cancel_command = {
"fundchannel_cancel",
"channels",
json_fund_channel_cancel,
"Cancel inflight channel establishment with peer {id}."
};
AUTODATA(json_command, &fund_channel_cancel_command);
static const struct json_command fund_channel_continue_command = {
"fundchannel_continue",
"channels",

3
openingd/opening_wire.csv

@ -98,6 +98,9 @@ opening_funder_continue,6012
opening_funder_continue,,funding_txid,struct bitcoin_txid
opening_funder_continue,,funding_txout,u16
#master->openingd: cancel channel establishment for a funding
opening_funder_cancel,6013
# Openingd->master: we failed to negotiation channel
opening_funder_failed,6004
opening_funder_failed,,reason,wirestring

Can't render this file because it has a wrong number of fields in line 6.

10
openingd/openingd.c

@ -1683,6 +1683,16 @@ static u8 *handle_master_in(struct state *state)
state->funding_txid = funding_txid;
state->funding_txout = funding_txout;
return funder_channel_continue(state);
case WIRE_OPENING_FUNDER_CANCEL:
/* We're aborting this, simple */
if (!fromwire_opening_funder_cancel(msg))
master_badmsg(WIRE_OPENING_FUNDER_CANCEL, msg);
msg = towire_errorfmt(NULL, &state->channel_id, "Channel open canceled by us");
sync_crypto_write(state->pps, take(msg));
negotiation_aborted(state, true,
"Channel open canceled by RPC", false);
return NULL;
case WIRE_OPENING_DEV_MEMLEAK:
#if DEVELOPER
handle_dev_memleak(state, msg);

8
tests/test_connection.py

@ -844,6 +844,14 @@ def test_funding_external_wallet_corners(node_factory, bitcoind):
with pytest.raises(RpcError, match=r'No channel funding in progress.'):
l1.rpc.fundchannel_continue(l2.info['id'], fake_txid, fake_txout)
l1.rpc.fundchannel_start(l2.info['id'], amount)
with pytest.raises(RpcError, match=r'Already funding channel'):
l1.rpc.fundchannel(l2.info['id'], amount)
l1.rpc.fundchannel_cancel(l2.info['id'])
# Should be able to 'restart' after canceling
l1.rpc.fundchannel_start(l2.info['id'], amount)
def test_funding_external_wallet(node_factory, bitcoind):
l1 = node_factory.get_node()

Loading…
Cancel
Save