mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-19 05:44:12 +01:00
json_fundchannel: fix release vs connect/nongossip race.
The new connect code revealed an existing race: we tell gossipd to release the peer, but at the same time it connects in. gossipd fails the release because the peer is remote, and json_fundchannel fails. Instead, we catch this race when we get peer_connected() and we were trying to open a channel. It means keeping a list of fundchannels which are awaiting a gossipd response though. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
bee795ed68
commit
8e976150ad
@ -66,6 +66,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
|
|||||||
ld->alias = NULL;
|
ld->alias = NULL;
|
||||||
ld->rgb = NULL;
|
ld->rgb = NULL;
|
||||||
list_head_init(&ld->connects);
|
list_head_init(&ld->connects);
|
||||||
|
list_head_init(&ld->fundchannels);
|
||||||
list_head_init(&ld->waitsendpay_commands);
|
list_head_init(&ld->waitsendpay_commands);
|
||||||
list_head_init(&ld->sendpay_commands);
|
list_head_init(&ld->sendpay_commands);
|
||||||
list_head_init(&ld->close_commands);
|
list_head_init(&ld->close_commands);
|
||||||
|
@ -125,6 +125,9 @@ struct lightningd {
|
|||||||
/* Outstanding connect commands. */
|
/* Outstanding connect commands. */
|
||||||
struct list_head connects;
|
struct list_head connects;
|
||||||
|
|
||||||
|
/* Outstanding fundchannel commands. */
|
||||||
|
struct list_head fundchannels;
|
||||||
|
|
||||||
/* Our chain topology. */
|
/* Our chain topology. */
|
||||||
struct chain_topology *topology;
|
struct chain_topology *topology;
|
||||||
|
|
||||||
|
@ -61,6 +61,9 @@ struct uncommitted_channel {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct funding_channel {
|
struct funding_channel {
|
||||||
|
/* In lightningd->fundchannels while waiting for gossipd reply. */
|
||||||
|
struct list_node list;
|
||||||
|
|
||||||
struct command *cmd; /* Which also owns us. */
|
struct command *cmd; /* Which also owns us. */
|
||||||
|
|
||||||
/* Peer we're trying to reach. */
|
/* Peer we're trying to reach. */
|
||||||
@ -77,6 +80,23 @@ struct funding_channel {
|
|||||||
struct uncommitted_channel *uc;
|
struct uncommitted_channel *uc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct funding_channel *find_funding_channel(struct lightningd *ld,
|
||||||
|
const struct pubkey *id)
|
||||||
|
{
|
||||||
|
struct funding_channel *i;
|
||||||
|
|
||||||
|
list_for_each(&ld->fundchannels, i, list) {
|
||||||
|
if (pubkey_eq(&i->peerid, id))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_funding_channel_from_list(struct funding_channel *fc)
|
||||||
|
{
|
||||||
|
list_del_from(&fc->cmd->ld->fundchannels, &fc->list);
|
||||||
|
}
|
||||||
|
|
||||||
/* Opening failed: hand back to gossipd (sending errpkt if not NULL) */
|
/* Opening failed: hand back to gossipd (sending errpkt if not NULL) */
|
||||||
static void uncommitted_channel_to_gossipd(struct lightningd *ld,
|
static void uncommitted_channel_to_gossipd(struct lightningd *ld,
|
||||||
struct uncommitted_channel *uc,
|
struct uncommitted_channel *uc,
|
||||||
@ -736,6 +756,10 @@ static void peer_offer_channel(struct lightningd *ld,
|
|||||||
u32 max_to_self_delay, max_minimum_depth;
|
u32 max_to_self_delay, max_minimum_depth;
|
||||||
u64 min_effective_htlc_capacity_msat;
|
u64 min_effective_htlc_capacity_msat;
|
||||||
|
|
||||||
|
/* Remove from list, it's not pending any more. */
|
||||||
|
list_del_from(&ld->fundchannels, &fc->list);
|
||||||
|
tal_del_destructor(fc, remove_funding_channel_from_list);
|
||||||
|
|
||||||
fc->uc = new_uncommitted_channel(ld, fc, &fc->peerid, addr);
|
fc->uc = new_uncommitted_channel(ld, fc, &fc->peerid, addr);
|
||||||
|
|
||||||
/* We asked to release this peer, but another raced in? Corner case,
|
/* We asked to release this peer, but another raced in? Corner case,
|
||||||
@ -809,6 +833,10 @@ static void gossip_peer_released(struct subd *gossip,
|
|||||||
struct channel *c;
|
struct channel *c;
|
||||||
struct uncommitted_channel *uc;
|
struct uncommitted_channel *uc;
|
||||||
|
|
||||||
|
/* handle_opening_channel might have already taken care of this. */
|
||||||
|
if (fc->uc)
|
||||||
|
return;
|
||||||
|
|
||||||
c = active_channel_by_id(ld, &fc->peerid, &uc);
|
c = active_channel_by_id(ld, &fc->peerid, &uc);
|
||||||
|
|
||||||
if (!fromwire_gossipctl_release_peer_reply(fc, resp, &addr, &cs,
|
if (!fromwire_gossipctl_release_peer_reply(fc, resp, &addr, &cs,
|
||||||
@ -840,6 +868,27 @@ static void gossip_peer_released(struct subd *gossip,
|
|||||||
fds[0], fds[1]);
|
fds[0], fds[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We can race: we're trying to get gossipd to release peer just as it
|
||||||
|
* reconnects. If that's happened, treat it as if it were
|
||||||
|
* released. */
|
||||||
|
bool handle_opening_channel(struct lightningd *ld,
|
||||||
|
const struct pubkey *id,
|
||||||
|
const struct wireaddr *addr,
|
||||||
|
const struct crypto_state *cs,
|
||||||
|
u64 gossip_index,
|
||||||
|
const u8 *gfeatures, const u8 *lfeatures,
|
||||||
|
int peer_fd, int gossip_fd)
|
||||||
|
{
|
||||||
|
struct funding_channel *fc = find_funding_channel(ld, id);
|
||||||
|
|
||||||
|
if (!fc)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
peer_offer_channel(ld, fc, addr, cs, gossip_index, gfeatures, lfeatures,
|
||||||
|
peer_fd, gossip_fd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* json_fund_channel - Entrypoint for funding a channel
|
* json_fund_channel - Entrypoint for funding a channel
|
||||||
*/
|
*/
|
||||||
@ -861,6 +910,7 @@ static void json_fund_channel(struct command *cmd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fc = tal(cmd, struct funding_channel);
|
fc = tal(cmd, struct funding_channel);
|
||||||
|
fc->uc = NULL;
|
||||||
fc->cmd = cmd;
|
fc->cmd = cmd;
|
||||||
fc->change_keyindex = 0;
|
fc->change_keyindex = 0;
|
||||||
fc->funding_satoshi = 0;
|
fc->funding_satoshi = 0;
|
||||||
@ -912,6 +962,9 @@ static void json_fund_channel(struct command *cmd,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list_add(&cmd->ld->fundchannels, &fc->list);
|
||||||
|
tal_add_destructor(fc, remove_funding_channel_from_list);
|
||||||
|
|
||||||
msg = towire_gossipctl_release_peer(cmd, &fc->peerid);
|
msg = towire_gossipctl_release_peer(cmd, &fc->peerid);
|
||||||
subd_req(fc, cmd->ld->gossip, msg, -1, 2, gossip_peer_released, fc);
|
subd_req(fc, cmd->ld->gossip, msg, -1, 2, gossip_peer_released, fc);
|
||||||
command_still_pending(cmd);
|
command_still_pending(cmd);
|
||||||
|
@ -29,6 +29,15 @@ u8 *peer_accept_channel(const tal_t *ctx,
|
|||||||
const struct channel_id *channel_id,
|
const struct channel_id *channel_id,
|
||||||
const u8 *open_msg);
|
const u8 *open_msg);
|
||||||
|
|
||||||
|
/* Gossipd spat out peer: were we currently asking gossipd to release it
|
||||||
|
* so we could open a channel? Returns true if it took over. */
|
||||||
|
bool handle_opening_channel(struct lightningd *ld,
|
||||||
|
const struct pubkey *id,
|
||||||
|
const struct wireaddr *addr,
|
||||||
|
const struct crypto_state *cs,
|
||||||
|
u64 gossip_index,
|
||||||
|
const u8 *gfeatures, const u8 *lfeatures,
|
||||||
|
int peer_fd, int gossip_fd);
|
||||||
|
|
||||||
void kill_uncommitted_channel(struct uncommitted_channel *uc,
|
void kill_uncommitted_channel(struct uncommitted_channel *uc,
|
||||||
const char *why);
|
const char *why);
|
||||||
|
@ -440,6 +440,11 @@ void peer_connected(struct lightningd *ld, const u8 *msg,
|
|||||||
goto send_error;
|
goto send_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Were we trying to open a channel, and we've raced? */
|
||||||
|
if (handle_opening_channel(ld, &id, &addr, &cs, gossip_index,
|
||||||
|
gfeatures, lfeatures, peer_fd, gossip_fd))
|
||||||
|
return;
|
||||||
|
|
||||||
/* If we're already dealing with this peer, hand off to correct
|
/* If we're already dealing with this peer, hand off to correct
|
||||||
* subdaemon. Otherwise, we'll respond iff they ask about an inactive
|
* subdaemon. Otherwise, we'll respond iff they ask about an inactive
|
||||||
* channel. */
|
* channel. */
|
||||||
|
@ -105,6 +105,15 @@ u8 *get_offered_global_features(const tal_t *ctx UNNEEDED)
|
|||||||
/* Generated stub for get_offered_local_features */
|
/* Generated stub for get_offered_local_features */
|
||||||
u8 *get_offered_local_features(const tal_t *ctx UNNEEDED)
|
u8 *get_offered_local_features(const tal_t *ctx UNNEEDED)
|
||||||
{ fprintf(stderr, "get_offered_local_features called!\n"); abort(); }
|
{ fprintf(stderr, "get_offered_local_features called!\n"); abort(); }
|
||||||
|
/* Generated stub for handle_opening_channel */
|
||||||
|
bool handle_opening_channel(struct lightningd *ld UNNEEDED,
|
||||||
|
const struct pubkey *id UNNEEDED,
|
||||||
|
const struct wireaddr *addr UNNEEDED,
|
||||||
|
const struct crypto_state *cs UNNEEDED,
|
||||||
|
u64 gossip_index UNNEEDED,
|
||||||
|
const u8 *gfeatures UNNEEDED, const u8 *lfeatures UNNEEDED,
|
||||||
|
int peer_fd UNNEEDED, int gossip_fd UNNEEDED)
|
||||||
|
{ fprintf(stderr, "handle_opening_channel called!\n"); abort(); }
|
||||||
/* Generated stub for invoices_autoclean_set */
|
/* Generated stub for invoices_autoclean_set */
|
||||||
void invoices_autoclean_set(struct invoices *invoices UNNEEDED,
|
void invoices_autoclean_set(struct invoices *invoices UNNEEDED,
|
||||||
u64 cycle_seconds UNNEEDED,
|
u64 cycle_seconds UNNEEDED,
|
||||||
|
Loading…
Reference in New Issue
Block a user