Browse Source

gossipd: keep reaching struct only when we're actively connecting, and don't retry

1. Lifetime of 'struct reaching' now only while we're actively doing connect.
2. Always free after a single attempt: if it's an important peer, retry
   on a timer.
3. Have a single response message to master, rather than relying on
   peer_connected on success and other msgs on failure.
4. If we are actively connecting and we get another command for the same
   id, just increment the counter

The result is much simpler in the master daemon, and much nicer for
reconnection: if they say to connect they get an immediate response,
rather than waiting for 10 retries.  Even if it's an important peer,
it fires off another reconnect attempt, unless it's actively
connecting now.

This removes exponential backoff: that's restored in next patch.  It
also doesn't handle multiple addresses for a single peer.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 7 years ago
parent
commit
72c459dd6c
  1. 192
      gossipd/gossip.c
  2. 27
      gossipd/gossip_wire.csv
  3. 90
      lightningd/connect_control.c
  4. 10
      lightningd/connect_control.h
  5. 11
      lightningd/gossip_control.c
  6. 25
      lightningd/peer_control.c
  7. 49
      tests/test_lightningd.py
  8. 14
      wallet/test/run-wallet.c

192
gossipd/gossip.c

@ -53,6 +53,7 @@
#define HSM_FD 3 #define HSM_FD 3
/* We put everything in this struct (redundantly) to pass it to timer cb */
struct important_peerid { struct important_peerid {
struct daemon *daemon; struct daemon *daemon;
@ -133,14 +134,11 @@ struct reaching {
/* The ID of the peer (not necessarily unique, in transit!) */ /* The ID of the peer (not necessarily unique, in transit!) */
struct pubkey id; struct pubkey id;
/* Where I'm reaching to. */ /* FIXME: Support multiple address. */
struct wireaddr addr; struct wireaddr addr;
/* How many times have we attempted to connect? */ /* How many (if any) connect commands are waiting for the result. */
u32 attempts; size_t num_master_responses;
/* Timestamp of the first attempt */
u32 first_attempt;
}; };
/* Things we need when we're talking direct to the peer. */ /* Things we need when we're talking direct to the peer. */
@ -306,17 +304,24 @@ static struct reaching *find_reaching(struct daemon *daemon,
static void reached_peer(struct peer *peer, struct io_conn *conn) static void reached_peer(struct peer *peer, struct io_conn *conn)
{ {
/* OK, we've reached the peer successfully, stop retrying. */ /* OK, we've reached the peer successfully, tell everyone. */
struct reaching *r = find_reaching(peer->daemon, &peer->id); struct reaching *r = find_reaching(peer->daemon, &peer->id);
u8 *msg;
if (!r) if (!r)
return; return;
/* Don't free conn with reach. */
tal_steal(peer->daemon, conn);
/* Don't call connect_failed */ /* Don't call connect_failed */
io_set_finish(conn, NULL, NULL); io_set_finish(conn, NULL, NULL);
/* Don't free conn with reach */
tal_steal(peer->daemon, conn);
/* Tell any connect commands what happened. */
msg = towire_gossipctl_connect_to_peer_result(r, &r->id, true, "");
for (size_t i = 0; i < r->num_master_responses; i++)
daemon_conn_send(&peer->daemon->master, msg);
tal_free(r); tal_free(r);
} }
@ -1596,37 +1601,32 @@ static struct io_plan *connection_out(struct io_conn *conn,
handshake_out_success, reach); handshake_out_success, reach);
} }
static void try_connect(struct reaching *reach);
static void connect_failed(struct io_conn *conn, struct reaching *reach) static void connect_failed(struct io_conn *conn, struct reaching *reach)
{ {
u32 diff = time_now().ts.tv_sec - reach->first_attempt; u8 *msg;
reach->attempts++; struct important_peerid *imp;
if (!important_peerid_map_get(&reach->daemon->important_peerids, &reach->id)
&& reach->attempts >= 10) {
status_info("Failed to connect after %d attempts, giving up "
"after %d seconds",
reach->attempts, diff);
daemon_conn_send(
&reach->daemon->master,
take(towire_gossip_peer_connection_failed(
NULL, &reach->id, diff, reach->attempts, false)));
tal_free(reach);
} else {
unsigned int secs;
/* Exponential backoff, then every 5 minutes */ /* Tell any connect commands what happened. */
if (reach->attempts < 9) msg = towire_gossipctl_connect_to_peer_result(reach, &reach->id,
secs = 1 << reach->attempts; false, strerror(errno));
else for (size_t i = 0; i < reach->num_master_responses; i++)
secs = 300; daemon_conn_send(&reach->daemon->master, msg);
status_trace("Failed connected out for %s, will try again in %u seconds",
type_to_string(tmpctx, struct pubkey, &reach->id), status_trace("Failed connected out for %s",
secs); type_to_string(tmpctx, struct pubkey, &reach->id));
new_reltimer(&reach->daemon->timers, reach, time_from_sec(secs),
try_connect, reach); /* If we want to keep trying, do so. */
imp = important_peerid_map_get(&reach->daemon->important_peerids,
&reach->id);
if (imp) {
/* FIXME: Exponential backoff! */
status_trace("...will try again in %u seconds", 5);
/* If important_id freed, this will be removed too */
new_reltimer(&reach->daemon->timers, imp,
time_from_sec(5), retry_important, imp);
} }
tal_free(reach);
return;
} }
static struct io_plan *conn_init(struct io_conn *conn, struct reaching *reach) static struct io_plan *conn_init(struct io_conn *conn, struct reaching *reach)
@ -1695,34 +1695,50 @@ seed_resolve_addr(const tal_t *ctx, const struct pubkey *id, const u16 port)
} }
} }
static void try_connect(struct reaching *reach) static void try_reach_peer(struct daemon *daemon, const struct pubkey *id,
bool master_needs_response)
{ {
struct addrhint *a; struct addrhint *a;
int fd; int fd;
struct reaching *reach;
u8 *msg;
struct peer *peer = find_peer(daemon, id);
/* Already succeeded somehow? */ if (peer) {
if (find_peer(reach->daemon, &reach->id)) { status_debug("try_reach_peer: have peer %s",
status_trace("Already reached %s, not retrying", type_to_string(tmpctx, struct pubkey, id));
type_to_string(tmpctx, struct pubkey, &reach->id)); if (master_needs_response) {
tal_free(reach); msg = towire_gossipctl_connect_to_peer_result(NULL, id,
true,
"");
daemon_conn_send(&daemon->master, take(msg));
}
return;
}
/* If we're trying to reach it right now, that's OK. */
reach = find_reaching(daemon, id);
if (reach) {
/* Please tell us too. */
if (master_needs_response)
reach->num_master_responses++;
return; return;
} }
a = find_addrhint(reach->daemon, &reach->id); a = find_addrhint(daemon, id);
if (!a) if (!a)
a = seed_resolve_addr(reach, &reach->id, 9735); a = seed_resolve_addr(tmpctx, id, 9735);
if (!a) { if (!a) {
status_info("No address known for %s, giving up", status_debug("No address known for %s, giving up",
type_to_string(tmpctx, struct pubkey, &reach->id)); type_to_string(tmpctx, struct pubkey, id));
daemon_conn_send( if (master_needs_response) {
&reach->daemon->master, msg = towire_gossipctl_connect_to_peer_result(NULL, id,
take(towire_gossip_peer_connection_failed( false,
NULL, &reach->id, "No address known, giving up");
time_now().ts.tv_sec - reach->first_attempt, daemon_conn_send(&daemon->master, take(msg));
reach->attempts, true))); }
tal_free(reach);
return; return;
} }
@ -1741,52 +1757,33 @@ static void try_connect(struct reaching *reach)
} }
if (fd < 0) { if (fd < 0) {
status_broken("Can't open %i socket for %s (%s), giving up", char *err = tal_fmt(tmpctx,
"Can't open %i socket for %s (%s), giving up",
a->addr.type, a->addr.type,
type_to_string(tmpctx, struct pubkey, &reach->id), type_to_string(tmpctx, struct pubkey, id),
strerror(errno)); strerror(errno));
tal_free(reach); status_debug("%s", err);
return; if (master_needs_response) {
} msg = towire_gossipctl_connect_to_peer_result(NULL, id,
false, err);
reach->addr = a->addr; daemon_conn_send(&daemon->master, take(msg));
io_new_conn(reach, fd, conn_init, reach);
}
/* Returns true if we're already connected. */
static bool try_reach_peer(struct daemon *daemon, const struct pubkey *id)
{
struct reaching *reach;
struct peer *peer;
reach = find_reaching(daemon, id);
if (reach) {
status_trace("try_reach_peer: already trying to reach %s",
type_to_string(tmpctx, struct pubkey, id));
return false;
} }
return;
/* Master might find out before we do that a peer is dead. */
peer = find_peer(daemon, id);
if (peer) {
status_trace("reach_peer: have peer %s",
type_to_string(tmpctx, struct pubkey, id));
return true;
} }
/* Start connecting to it */
reach = tal(daemon, struct reaching); reach = tal(daemon, struct reaching);
reach->daemon = daemon; reach->daemon = daemon;
reach->id = *id; reach->id = *id;
reach->first_attempt = time_now().ts.tv_sec; reach->addr = a->addr;
reach->attempts = 0; reach->num_master_responses = master_needs_response;
list_add_tail(&daemon->reaching, &reach->list); list_add_tail(&daemon->reaching, &reach->list);
tal_add_destructor(reach, destroy_reaching); tal_add_destructor(reach, destroy_reaching);
try_connect(reach); io_new_conn(reach, fd, conn_init, reach);
return false;
} }
/* Reconnect to peer. */ /* Called from timer, so needs single-arg declaration */
static void retry_important(struct important_peerid *imp) static void retry_important(struct important_peerid *imp)
{ {
#if DEVELOPER #if DEVELOPER
@ -1795,24 +1792,18 @@ static void retry_important(struct important_peerid *imp)
if (imp->daemon->no_reconnect) if (imp->daemon->no_reconnect)
return; return;
#endif #endif
try_reach_peer(imp->daemon, &imp->id); try_reach_peer(imp->daemon, &imp->id, false);
} }
static struct io_plan *reach_peer(struct io_conn *conn, static struct io_plan *connect_to_peer(struct io_conn *conn,
struct daemon *daemon, const u8 *msg) struct daemon *daemon, const u8 *msg)
{ {
struct pubkey id; struct pubkey id;
if (!fromwire_gossipctl_reach_peer(msg, &id)) if (!fromwire_gossipctl_connect_to_peer(msg, &id))
master_badmsg(WIRE_GOSSIPCTL_REACH_PEER, msg); master_badmsg(WIRE_GOSSIPCTL_CONNECT_TO_PEER, msg);
/* Master can't check this itself, because that's racy. */
if (try_reach_peer(daemon, &id)) {
daemon_conn_send(&daemon->master,
take(towire_gossip_peer_already_connected(NULL,
&id)));
}
try_reach_peer(daemon, &id, true);
return daemon_conn_read_next(conn, &daemon->master); return daemon_conn_read_next(conn, &daemon->master);
} }
@ -2108,8 +2099,8 @@ static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master
case WIRE_GOSSIPCTL_HAND_BACK_PEER: case WIRE_GOSSIPCTL_HAND_BACK_PEER:
return hand_back_peer(conn, daemon, master->msg_in); return hand_back_peer(conn, daemon, master->msg_in);
case WIRE_GOSSIPCTL_REACH_PEER: case WIRE_GOSSIPCTL_CONNECT_TO_PEER:
return reach_peer(conn, daemon, master->msg_in); return connect_to_peer(conn, daemon, master->msg_in);
case WIRE_GOSSIPCTL_PEER_ADDRHINT: case WIRE_GOSSIPCTL_PEER_ADDRHINT:
return addr_hint(conn, daemon, master->msg_in); return addr_hint(conn, daemon, master->msg_in);
@ -2149,8 +2140,7 @@ static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master
case WIRE_GOSSIP_PING_REPLY: case WIRE_GOSSIP_PING_REPLY:
case WIRE_GOSSIP_RESOLVE_CHANNEL_REPLY: case WIRE_GOSSIP_RESOLVE_CHANNEL_REPLY:
case WIRE_GOSSIP_PEER_CONNECTED: case WIRE_GOSSIP_PEER_CONNECTED:
case WIRE_GOSSIP_PEER_ALREADY_CONNECTED: case WIRE_GOSSIPCTL_CONNECT_TO_PEER_RESULT:
case WIRE_GOSSIP_PEER_CONNECTION_FAILED:
case WIRE_GOSSIP_PEER_NONGOSSIP: case WIRE_GOSSIP_PEER_NONGOSSIP:
case WIRE_GOSSIP_GET_UPDATE: case WIRE_GOSSIP_GET_UPDATE:
case WIRE_GOSSIP_GET_UPDATE_REPLY: case WIRE_GOSSIP_GET_UPDATE_REPLY:

27
gossipd/gossip_wire.csv

@ -29,10 +29,18 @@ gossipctl_peer_addrhint,3014
gossipctl_peer_addrhint,,id,struct pubkey gossipctl_peer_addrhint,,id,struct pubkey
gossipctl_peer_addrhint,,addr,struct wireaddr gossipctl_peer_addrhint,,addr,struct wireaddr
# Master -> gossipd: connect to a peer. We may get a peer_connected or # Master -> gossipd: connect to a peer.
# peer_already_connected gossipctl_connect_to_peer,3001
gossipctl_reach_peer,3001 gossipctl_connect_to_peer,,id,struct pubkey
gossipctl_reach_peer,,id,struct pubkey
# Gossipd->master: result (not a reply since it can be out-of-order, but
# you will get one reply for every request).
gossipctl_connect_to_peer_result,3020
gossipctl_connect_to_peer_result,,id,struct pubkey
# True it connected.
gossipctl_connect_to_peer_result,,connected,bool
# Otherwise, why we can't reach them.
gossipctl_connect_to_peer_result,,failreason,wirestring
# Master -> gossipd: try to always maintain connection to this peer (or not) # Master -> gossipd: try to always maintain connection to this peer (or not)
gossipctl_peer_important,3010 gossipctl_peer_important,3010
@ -50,17 +58,6 @@ gossip_peer_connected,,gfeatures,gflen*u8
gossip_peer_connected,,lflen,u16 gossip_peer_connected,,lflen,u16
gossip_peer_connected,,lfeatures,lflen*u8 gossip_peer_connected,,lfeatures,lflen*u8
# Gossipd -> master: you asked to reach a peer, we already had.
gossip_peer_already_connected,3015
gossip_peer_already_connected,,id,struct pubkey
# gossipd -> master: attempted to connect, unsuccessful, gave up
gossip_peer_connection_failed,3020
gossip_peer_connection_failed,,id,struct pubkey
gossip_peer_connection_failed,,timeout,u32
gossip_peer_connection_failed,,attempts,u32
gossip_peer_connection_failed,,addr_unknown,bool
# Gossipd -> master: peer sent non-gossip packet. Two fds: peer and gossip # Gossipd -> master: peer sent non-gossip packet. Two fds: peer and gossip
gossip_peer_nongossip,3003 gossip_peer_nongossip,3003
gossip_peer_nongossip,,id,struct pubkey gossip_peer_nongossip,,id,struct pubkey

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

90
lightningd/connect_control.c

@ -28,79 +28,51 @@ static struct connect *new_connect(struct lightningd *ld,
struct connect *c = tal(cmd, struct connect); struct connect *c = tal(cmd, struct connect);
c->id = *id; c->id = *id;
c->cmd = cmd; c->cmd = cmd;
list_add(&ld->connects, &c->list); list_add_tail(&ld->connects, &c->list);
tal_add_destructor(c, destroy_connect); tal_add_destructor(c, destroy_connect);
return c; return c;
} }
void connect_succeeded(struct lightningd *ld, const struct pubkey *id) /* Finds first command which matches. */
static struct connect *find_connect(struct lightningd *ld,
const struct pubkey *id)
{ {
struct connect *i, *next; struct connect *i;
/* Careful! Completing command frees connect. */ list_for_each(&ld->connects, i, list) {
list_for_each_safe(&ld->connects, i, next, list) {
struct json_result *response;
if (!pubkey_eq(&i->id, id))
continue;
response = new_json_result(i->cmd);
json_object_start(response, NULL);
json_add_pubkey(response, "id", id);
json_object_end(response);
command_success(i->cmd, response);
}
}
void connect_failed(struct lightningd *ld, const struct pubkey *id,
const char *error)
{
struct connect *i, *next;
/* Careful! Completing command frees connect. */
list_for_each_safe(&ld->connects, i, next, list) {
if (pubkey_eq(&i->id, id)) if (pubkey_eq(&i->id, id))
command_fail(i->cmd, "%s", error); return i;
} }
return NULL;
} }
void peer_connection_failed(struct lightningd *ld, const u8 *msg) void gossip_connect_result(struct lightningd *ld, const u8 *msg)
{ {
struct pubkey id; struct pubkey id;
u32 attempts, timediff; bool connected;
bool addr_unknown; char *err;
char *error; struct connect *c;
if (!fromwire_gossip_peer_connection_failed(msg, &id, &timediff, if (!fromwire_gossipctl_connect_to_peer_result(tmpctx, msg,
&attempts, &addr_unknown)) &id,
fatal( &connected,
"Gossip gave bad GOSSIP_PEER_CONNECTION_FAILED message %s", &err))
fatal("Gossip gave bad GOSSIPCTL_CONNECT_TO_PEER_RESULT message %s",
tal_hex(msg, msg)); tal_hex(msg, msg));
if (addr_unknown) {
error = tal_fmt(
msg, "No address known for node %s, please provide one",
type_to_string(msg, struct pubkey, &id));
} else {
error = tal_fmt(msg, "Could not connect to %s after %d seconds and %d attempts",
type_to_string(msg, struct pubkey, &id), timediff,
attempts);
}
connect_failed(ld, &id, error); c = find_connect(ld, &id);
} assert(c);
/* Gossipd tells us peer was already connected. */
void peer_already_connected(struct lightningd *ld, const u8 *msg)
{
struct pubkey id;
if (!fromwire_gossip_peer_already_connected(msg, &id))
fatal("Gossip gave bad GOSSIP_PEER_ALREADY_CONNECTED message %s",
tal_hex(msg, msg));
/* If we were waiting for connection, we succeeded. */ if (connected) {
connect_succeeded(ld, &id); struct json_result *response = new_json_result(c->cmd);
json_object_start(response, NULL);
json_add_pubkey(response, "id", &id);
json_object_end(response);
command_success(c->cmd, response);
} else {
command_fail(c->cmd, "%s", err);
}
} }
static void json_connect(struct command *cmd, static void json_connect(struct command *cmd,
@ -191,10 +163,10 @@ static void json_connect(struct command *cmd,
} }
/* Now tell it to try reaching it. */ /* Now tell it to try reaching it. */
msg = towire_gossipctl_reach_peer(cmd, &id); msg = towire_gossipctl_connect_to_peer(NULL, &id);
subd_send_msg(cmd->ld->gossip, take(msg)); subd_send_msg(cmd->ld->gossip, take(msg));
/* Leave this here for gossip_peer_connected */ /* Leave this here for gossip_connect_result */
new_connect(cmd->ld, &id, cmd); new_connect(cmd->ld, &id, cmd);
command_still_pending(cmd); command_still_pending(cmd);
} }

10
lightningd/connect_control.h

@ -5,14 +5,6 @@
struct lightningd; struct lightningd;
struct pubkey; struct pubkey;
void connect_succeeded(struct lightningd *ld, const struct pubkey *id); void gossip_connect_result(struct lightningd *ld, const u8 *msg);
void connect_failed(struct lightningd *ld, const struct pubkey *id,
const char *error);
/* Gossipd was unable to connect to the peer */
void peer_connection_failed(struct lightningd *ld, const u8 *msg);
/* This simply means we asked to reach a peer, but we already have it */
void peer_already_connected(struct lightningd *ld, const u8 *msg);
#endif /* LIGHTNING_LIGHTNINGD_CONNECT_CONTROL_H */ #endif /* LIGHTNING_LIGHTNINGD_CONNECT_CONTROL_H */

11
lightningd/gossip_control.c

@ -125,7 +125,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds)
case WIRE_GOSSIP_GETPEERS_REQUEST: case WIRE_GOSSIP_GETPEERS_REQUEST:
case WIRE_GOSSIP_PING: case WIRE_GOSSIP_PING:
case WIRE_GOSSIP_RESOLVE_CHANNEL_REQUEST: case WIRE_GOSSIP_RESOLVE_CHANNEL_REQUEST:
case WIRE_GOSSIPCTL_REACH_PEER: case WIRE_GOSSIPCTL_CONNECT_TO_PEER:
case WIRE_GOSSIPCTL_HAND_BACK_PEER: case WIRE_GOSSIPCTL_HAND_BACK_PEER:
case WIRE_GOSSIPCTL_RELEASE_PEER: case WIRE_GOSSIPCTL_RELEASE_PEER:
case WIRE_GOSSIPCTL_PEER_ADDRHINT: case WIRE_GOSSIPCTL_PEER_ADDRHINT:
@ -160,12 +160,6 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds)
return 2; return 2;
peer_connected(gossip->ld, msg, fds[0], fds[1]); peer_connected(gossip->ld, msg, fds[0], fds[1]);
break; break;
case WIRE_GOSSIP_PEER_ALREADY_CONNECTED:
peer_already_connected(gossip->ld, msg);
break;
case WIRE_GOSSIP_PEER_CONNECTION_FAILED:
peer_connection_failed(gossip->ld, msg);
break;
case WIRE_GOSSIP_PEER_NONGOSSIP: case WIRE_GOSSIP_PEER_NONGOSSIP:
if (tal_count(fds) != 2) if (tal_count(fds) != 2)
return 2; return 2;
@ -174,6 +168,9 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds)
case WIRE_GOSSIP_GET_TXOUT: case WIRE_GOSSIP_GET_TXOUT:
get_txout(gossip, msg); get_txout(gossip, msg);
break; break;
case WIRE_GOSSIPCTL_CONNECT_TO_PEER_RESULT:
gossip_connect_result(gossip->ld, msg);
break;
} }
return 0; return 0;
} }

25
lightningd/peer_control.c

@ -492,7 +492,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg,
peer_start_channeld(channel, &cs, gossip_index, peer_start_channeld(channel, &cs, gossip_index,
peer_fd, gossip_fd, NULL, peer_fd, gossip_fd, NULL,
true); true);
goto connected; return;
case CLOSINGD_SIGEXCHANGE: case CLOSINGD_SIGEXCHANGE:
/* Stop any existing daemon, without triggering error /* Stop any existing daemon, without triggering error
@ -503,26 +503,17 @@ void peer_connected(struct lightningd *ld, const u8 *msg,
peer_start_closingd(channel, &cs, gossip_index, peer_start_closingd(channel, &cs, gossip_index,
peer_fd, gossip_fd, peer_fd, gossip_fd,
true, NULL); true, NULL);
goto connected; return;
} }
abort(); abort();
} }
return_to_gossipd: return_to_gossipd:
/* Otherwise, we hand back to gossipd, to continue. */ /* No err, all good. */
msg = towire_gossipctl_hand_back_peer(msg, &id, &cs, gossip_index, NULL); error = NULL;
subd_send_msg(ld->gossip, take(msg));
subd_send_fd(ld->gossip, peer_fd);
subd_send_fd(ld->gossip, gossip_fd);
connected:
/* If we were waiting for connection, we succeeded. */
connect_succeeded(ld, &id);
return;
send_error: send_error:
/* Hand back to gossipd, with an error packet. */ /* Hand back to gossipd, with an error packet. */
connect_failed(ld, &id, sanitize_error(msg, error, NULL));
msg = towire_gossipctl_hand_back_peer(msg, &id, &cs, gossip_index, msg = towire_gossipctl_hand_back_peer(msg, &id, &cs, gossip_index,
error); error);
subd_send_msg(ld->gossip, take(msg)); subd_send_msg(ld->gossip, take(msg));
@ -610,7 +601,6 @@ void peer_sent_nongossip(struct lightningd *ld,
send_error: send_error:
/* Hand back to gossipd, with an error packet. */ /* Hand back to gossipd, with an error packet. */
connect_failed(ld, id, sanitize_error(tmpctx, error, NULL));
msg = towire_gossipctl_hand_back_peer(ld, id, cs, gossip_index, error); msg = towire_gossipctl_hand_back_peer(ld, id, cs, gossip_index, error);
subd_send_msg(ld->gossip, take(msg)); subd_send_msg(ld->gossip, take(msg));
subd_send_fd(ld->gossip, peer_fd); subd_send_fd(ld->gossip, peer_fd);
@ -1068,13 +1058,6 @@ static void json_close(struct command *cmd,
subd_send_msg(channel->owner, subd_send_msg(channel->owner,
take(towire_channel_send_shutdown(channel))); take(towire_channel_send_shutdown(channel)));
} }
/* If channel has no owner, it means the peer is disconnected,
* so make a nominal effort to contact it now.
*/
if (!channel->owner)
subd_send_msg(cmd->ld->gossip,
take(towire_gossipctl_reach_peer(NULL,
&channel->peer->id)));
/* Register this command for later handling. */ /* Register this command for later handling. */
register_close_command(cmd->ld, cmd, channel, timeout, force); register_close_command(cmd->ld, cmd, channel, timeout, force);

49
tests/test_lightningd.py

@ -78,7 +78,9 @@ def wait_forget_channels(node):
"""This node is closing all of its channels, check we are forgetting them """This node is closing all of its channels, check we are forgetting them
""" """
node.daemon.wait_for_log(r'onchaind complete, forgetting peer') node.daemon.wait_for_log(r'onchaind complete, forgetting peer')
assert node.rpc.listpeers()['peers'] == [] # May have reconnected, but should merely be gossiping.
for peer in node.rpc.listpeers()['peers']:
assert peer['state'] == 'GOSSIPING'
assert node.db_query("SELECT * FROM channels") == [] assert node.db_query("SELECT * FROM channels") == []
@ -616,6 +618,11 @@ class LightningDTests(BaseLightningDTests):
assert len(l1.rpc.listpeers()) == 1 assert len(l1.rpc.listpeers()) == 1
assert len(l2.rpc.listpeers()) == 1 assert len(l2.rpc.listpeers()) == 1
# Should get reasonable error if unknown addr for peer.
self.assertRaisesRegex(ValueError,
"No address known",
l1.rpc.connect, '032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e')
def test_connect_standard_addr(self): def test_connect_standard_addr(self):
"""Test standard node@host:port address """Test standard node@host:port address
""" """
@ -635,6 +642,34 @@ class LightningDTests(BaseLightningDTests):
# ret = l1.rpc.connect("{}@[::1]:{}".format(l3.info['id'], l3.info['port'])) # ret = l1.rpc.connect("{}@[::1]:{}".format(l3.info['id'], l3.info['port']))
# assert ret['id'] == l3.info['id'] # assert ret['id'] == l3.info['id']
def test_reconnect_channel_peers(self):
l1 = self.node_factory.get_node(may_reconnect=True)
l2 = self.node_factory.get_node(may_reconnect=True)
l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port'])
self.fund_channel(l1, l2, 10**6)
l2.stop()
l2.daemon.start()
# Should reconnect.
wait_for(lambda: l1.rpc.listpeers(l2.info['id'])['peers'][0]['connected'])
wait_for(lambda: l2.rpc.listpeers(l1.info['id'])['peers'][0]['connected'])
# Connect command should succeed.
l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port'])
# Stop l2 and wait for l1 to notice.
l2.stop()
wait_for(lambda: not l1.rpc.listpeers(l2.info['id'])['peers'][0]['connected'])
# Now should fail.
self.assertRaisesRegex(ValueError,
"Connection refused",
l1.rpc.connect, l2.info['id'], 'localhost', l2.info['port'])
# It should now succeed when it restarts.
l2.daemon.start()
l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port'])
def test_balance(self): def test_balance(self):
l1, l2 = self.connect() l1, l2 = self.connect()
@ -2961,18 +2996,24 @@ class LightningDTests(BaseLightningDTests):
@unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1")
def test_disconnect(self): def test_disconnect(self):
# These should all make us fail, and retry. # These should all make us fail
# FIXME: Configure short timeout for reconnect!
disconnects = ['-WIRE_INIT', disconnects = ['-WIRE_INIT',
'@WIRE_INIT', '@WIRE_INIT',
'+WIRE_INIT'] '+WIRE_INIT']
l1 = self.node_factory.get_node(disconnect=disconnects) l1 = self.node_factory.get_node(disconnect=disconnects)
l2 = self.node_factory.get_node() l2 = self.node_factory.get_node()
self.assertRaises(ValueError, l1.rpc.connect,
l2.info['id'], 'localhost', l2.info['port'])
self.assertRaises(ValueError, l1.rpc.connect,
l2.info['id'], 'localhost', l2.info['port'])
self.assertRaises(ValueError, l1.rpc.connect,
l2.info['id'], 'localhost', l2.info['port'])
l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port'])
# Should have 3 connect fails. # Should have 3 connect fails.
for d in disconnects: for d in disconnects:
l1.daemon.wait_for_log('Failed connected out for {}, will try again' l1.daemon.wait_for_log('Failed connected out for {}'
.format(l2.info['id'])) .format(l2.info['id']))
# Should still only have one peer! # Should still only have one peer!

14
wallet/test/run-wallet.c

@ -58,13 +58,6 @@ void command_still_pending(struct command *cmd UNNEEDED)
/* Generated stub for command_success */ /* Generated stub for command_success */
void command_success(struct command *cmd UNNEEDED, struct json_result *response UNNEEDED) void command_success(struct command *cmd UNNEEDED, struct json_result *response UNNEEDED)
{ fprintf(stderr, "command_success called!\n"); abort(); } { fprintf(stderr, "command_success called!\n"); abort(); }
/* Generated stub for connect_failed */
void connect_failed(struct lightningd *ld UNNEEDED, const struct pubkey *id UNNEEDED,
const char *error UNNEEDED)
{ fprintf(stderr, "connect_failed called!\n"); abort(); }
/* Generated stub for connect_succeeded */
void connect_succeeded(struct lightningd *ld UNNEEDED, const struct pubkey *id UNNEEDED)
{ fprintf(stderr, "connect_succeeded called!\n"); abort(); }
/* Generated stub for derive_basepoints */ /* Generated stub for derive_basepoints */
bool derive_basepoints(const struct privkey *seed UNNEEDED, bool derive_basepoints(const struct privkey *seed UNNEEDED,
struct pubkey *funding_pubkey UNNEEDED, struct pubkey *funding_pubkey UNNEEDED,
@ -343,10 +336,6 @@ void peer_start_closingd(struct channel *channel UNNEEDED,
bool reconnected UNNEEDED, bool reconnected UNNEEDED,
const u8 *channel_reestablish UNNEEDED) const u8 *channel_reestablish UNNEEDED)
{ fprintf(stderr, "peer_start_closingd called!\n"); abort(); } { fprintf(stderr, "peer_start_closingd called!\n"); abort(); }
/* Generated stub for sanitize_error */
char *sanitize_error(const tal_t *ctx UNNEEDED, const u8 *errmsg UNNEEDED,
struct channel_id *channel_id UNNEEDED)
{ fprintf(stderr, "sanitize_error called!\n"); abort(); }
/* Generated stub for subd_release_channel */ /* Generated stub for subd_release_channel */
void subd_release_channel(struct subd *owner UNNEEDED, void *channel UNNEEDED) void subd_release_channel(struct subd *owner UNNEEDED, void *channel UNNEEDED)
{ fprintf(stderr, "subd_release_channel called!\n"); abort(); } { fprintf(stderr, "subd_release_channel called!\n"); abort(); }
@ -394,9 +383,6 @@ u8 *towire_gossipctl_peer_disconnect(const tal_t *ctx UNNEEDED, const struct pub
/* Generated stub for towire_gossipctl_peer_important */ /* Generated stub for towire_gossipctl_peer_important */
u8 *towire_gossipctl_peer_important(const tal_t *ctx UNNEEDED, const struct pubkey *id UNNEEDED, bool important UNNEEDED) u8 *towire_gossipctl_peer_important(const tal_t *ctx UNNEEDED, const struct pubkey *id UNNEEDED, bool important UNNEEDED)
{ fprintf(stderr, "towire_gossipctl_peer_important called!\n"); abort(); } { fprintf(stderr, "towire_gossipctl_peer_important called!\n"); abort(); }
/* Generated stub for towire_gossipctl_reach_peer */
u8 *towire_gossipctl_reach_peer(const tal_t *ctx UNNEEDED, const struct pubkey *id UNNEEDED)
{ fprintf(stderr, "towire_gossipctl_reach_peer called!\n"); abort(); }
/* Generated stub for towire_gossip_disable_channel */ /* Generated stub for towire_gossip_disable_channel */
u8 *towire_gossip_disable_channel(const tal_t *ctx UNNEEDED, const struct short_channel_id *short_channel_id UNNEEDED, u8 direction UNNEEDED, bool active UNNEEDED) u8 *towire_gossip_disable_channel(const tal_t *ctx UNNEEDED, const struct short_channel_id *short_channel_id UNNEEDED, u8 direction UNNEEDED, bool active UNNEEDED)
{ fprintf(stderr, "towire_gossip_disable_channel called!\n"); abort(); } { fprintf(stderr, "towire_gossip_disable_channel called!\n"); abort(); }

Loading…
Cancel
Save