From d822ba1eee3f03cb453ee78af0da5c8479b53204 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 12 Feb 2018 20:43:04 +1030 Subject: [PATCH] lightningd: allow a new channel open from peer if no *active* channels. And return the correct error message for the channel they give, if they try to re-establish on an error channel. Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 80 ++++++++++++++++++++++++--------------- tests/test_lightningd.py | 24 ++++++++++++ 2 files changed, 74 insertions(+), 30 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 8e119a80d..4f943d54f 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -89,12 +89,6 @@ static void peer_accept_channel(struct lightningd *ld, int peer_fd, int gossip_fd, const u8 *open_msg); -/* FIXME: This is here until we get multi-channel support */ -static struct channel *peer2channel(const struct peer *peer) -{ - return list_top(&peer->channels, struct channel, list); -} - static void destroy_peer(struct peer *peer) { list_del_from(&peer->ld->peers, &peer->list); @@ -287,7 +281,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg, u8 *error; u8 *supported_global_features; u8 *supported_local_features; - struct peer *peer; + struct channel *channel; struct wireaddr addr; u64 gossip_index; @@ -316,16 +310,15 @@ void peer_connected(struct lightningd *ld, const u8 *msg, goto send_error; } - /* Now, do we already know this peer? */ - peer = peer_by_id(ld, &id); - if (peer) { - /* FIXME: Only consider active channels! */ - struct channel *channel = peer2channel(peer); - + /* If we're already dealing with this peer, hand off to correct + * subdaemon. Otherwise, we'll respond iff they ask about an inactive + * channel. */ + channel = active_channel_by_id(ld, &id); + if (channel) { log_debug(channel->log, "Peer has reconnected, state %s", channel_state_name(channel)); - /* FIXME: We can have errors for multiple channels. */ + /* If we have a canned error, deliver it now. */ if (channel->error) { error = channel->error; goto send_error; @@ -355,9 +348,9 @@ void peer_connected(struct lightningd *ld, const u8 *msg, case ONCHAIND_OUR_UNILATERAL: case FUNDING_SPEND_SEEN: case ONCHAIND_MUTUAL: - /* If they try to reestablish channel, we'll send - * error then */ - goto return_to_gossipd; + case CLOSINGD_COMPLETE: + /* Channel is active! */ + abort(); case CHANNELD_AWAITING_LOCKIN: case CHANNELD_NORMAL: @@ -366,19 +359,18 @@ void peer_connected(struct lightningd *ld, const u8 *msg, * on this peer. */ channel_set_owner(channel, NULL); - peer->addr = addr; + channel->peer->addr = addr; peer_start_channeld(channel, &cs, gossip_index, peer_fd, gossip_fd, NULL, true); goto connected; case CLOSINGD_SIGEXCHANGE: - case CLOSINGD_COMPLETE: /* Stop any existing daemon, without triggering error * on this peer. */ channel_set_owner(channel, NULL); - peer->addr = addr; + channel->peer->addr = addr; peer_start_closingd(channel, &cs, gossip_index, peer_fd, gossip_fd, true); @@ -455,6 +447,27 @@ void peer_connection_failed(struct lightningd *ld, const u8 *msg) } } +static struct channel *channel_by_channel_id(struct peer *peer, + const struct channel_id *channel_id) +{ + struct channel *channel; + + list_for_each(&peer->channels, channel, list) { + struct channel_id cid; + + if (!channel->funding_txid) + continue; + + derive_channel_id(&cid, + channel->funding_txid, + channel->funding_outnum); + if (structeq(&cid, channel_id)) + return channel; + } + return NULL; +} + +/* We only get here IF we weren't trying to connect to it. */ void peer_sent_nongossip(struct lightningd *ld, const struct pubkey *id, const struct wireaddr *addr, @@ -474,28 +487,35 @@ void peer_sent_nongossip(struct lightningd *ld, else channel_id = &extracted_channel_id; - /* FIXME: match state too; we can have multiple onchain - * (ie. dead) channels for the same peer. */ peer = peer_by_id(ld, id); - if (peer) { - error = towire_errorfmt(ld, channel_id, - "Unexpected message %i in state %s", - fromwire_peektype(in_msg), - channel_state_name(peer2channel(peer))); - goto send_error; - } /* Open request? */ if (fromwire_peektype(in_msg) == WIRE_OPEN_CHANNEL) { + if (peer && peer_active_channel(peer)) { + error = towire_errorfmt(in_msg, channel_id, + "Multiple channels unsupported"); + goto send_error; + } peer_accept_channel(ld, id, addr, cs, gossip_index, gfeatures, lfeatures, peer_fd, gossip_fd, in_msg); return; } + /* If they are talking about a specific channel id, we may have an + * error for them. */ + if (peer && channel_id) { + struct channel *channel; + channel = channel_by_channel_id(peer, channel_id); + if (channel && channel->error) { + error = channel->error; + goto send_error; + } + } + /* Weird request. */ error = towire_errorfmt(ld, channel_id, - "Unexpected message %i for unknown peer", + "Unexpected message %i for peer", fromwire_peektype(in_msg)); send_error: diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 2b99bbf20..be6b510c4 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -3382,6 +3382,30 @@ class LightningDTests(BaseLightningDTests): oneconfig = l1.rpc.listconfigs(c) assert(oneconfig[c] == configs[c]) + def test_multiple_channels(self): + l1 = self.node_factory.get_node() + l2 = self.node_factory.get_node() + + for i in range(3): + # FIXME: we shouldn't disconnect on close? + ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + assert ret['id'] == l2.info['id'] + + l1.daemon.wait_for_log('WIRE_GOSSIPCTL_HAND_BACK_PEER') + l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HAND_BACK_PEER') + chanid = self.fund_channel(l1, l2, 10**6) + + l1.rpc.close(l2.info['id']) + l1.daemon.wait_for_log(' to CLOSINGD_COMPLETE') + l2.daemon.wait_for_log(' to CLOSINGD_COMPLETE') + + channels = l1.rpc.listpeers()['peers'][0]['channels'] + assert len(channels) == 3 + # Most in state ONCHAIND_MUTUAL, last is CLOSINGD_COMPLETE + for i in range(len(channels)-1): + assert channels[i]['state'] == 'ONCHAIND_MUTUAL' + assert channels[-1]['state'] == 'CLOSINGD_COMPLETE' + def test_cli(self): l1 = self.node_factory.get_node()