mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-24 01:24:26 +01:00
gossipd: hand back peer, don't hand a new peer.
All peers come from gossipd, and maintain an fd to talk to it. Sometimes we hand the peer back, but to avoid a race, we always recreated it. The race was that a daemon closed the gossip_fd, which made gossipd forget the peer, then master handed the peer back to gossipd. We stop the race by never closing the gossipfd, but hand it back to gossipd for closing. Now gossipd has to accept two fds, but the handling of peers is far clearer. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
111
gossipd/gossip.c
111
gossipd/gossip.c
@@ -827,55 +827,80 @@ static struct io_plan *new_peer_got_fd(struct io_conn *conn, struct peer *peer)
|
|||||||
return daemon_conn_read_next(conn, &peer->daemon->master);
|
return daemon_conn_read_next(conn, &peer->daemon->master);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read and close fd */
|
/* This lets us read the fds in before handling anything. */
|
||||||
static struct io_plan *discard_peer_fd(struct io_conn *conn, int *fd)
|
struct returning_peer {
|
||||||
{
|
struct daemon *daemon;
|
||||||
struct daemon *daemon = tal_parent(fd);
|
|
||||||
close(*fd);
|
|
||||||
tal_free(fd);
|
|
||||||
return daemon_conn_read_next(conn, &daemon->master);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct io_plan *handle_peer(struct io_conn *conn, struct daemon *daemon,
|
|
||||||
const u8 *msg)
|
|
||||||
{
|
|
||||||
struct peer *peer;
|
|
||||||
struct crypto_state cs;
|
|
||||||
struct pubkey id;
|
struct pubkey id;
|
||||||
struct wireaddr addr;
|
struct crypto_state cs;
|
||||||
u8 *gfeatures, *lfeatures;
|
|
||||||
u8 *inner_msg;
|
u8 *inner_msg;
|
||||||
|
int peer_fd, gossip_fd;
|
||||||
|
};
|
||||||
|
|
||||||
if (!fromwire_gossipctl_handle_peer(msg, msg, NULL, &id, &addr, &cs,
|
static struct io_plan *handle_returning_peer(struct io_conn *conn,
|
||||||
&gfeatures, &lfeatures, &inner_msg))
|
struct returning_peer *rpeer)
|
||||||
master_badmsg(WIRE_GOSSIPCTL_HANDLE_PEER, msg);
|
{
|
||||||
|
struct daemon *daemon = rpeer->daemon;
|
||||||
|
struct peer *peer;
|
||||||
|
|
||||||
/* If it already exists locally, that's probably a reconnect:
|
peer = find_peer(daemon, &rpeer->id);
|
||||||
* drop this one. If it exists as remote, replace with this.*/
|
if (!peer)
|
||||||
peer = find_peer(daemon, &id);
|
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||||
if (peer) {
|
"hand_back_peer unknown peer: %s",
|
||||||
if (peer->local) {
|
type_to_string(trc, struct pubkey, &rpeer->id));
|
||||||
int *fd = tal(daemon, int);
|
|
||||||
status_trace("handle_peer %s: duplicate, dropping",
|
/* We don't need the gossip_fd. We could drain it, so no gossip msgs
|
||||||
type_to_string(trc, struct pubkey, &id));
|
* are missed, but that seems overkill. */
|
||||||
return io_recv_fd(conn, fd, discard_peer_fd, fd);
|
close(rpeer->gossip_fd);
|
||||||
}
|
|
||||||
status_trace("handle_peer %s: found remote duplicate, dropping",
|
/* Possible if there's a reconnect: ignore handed back. */
|
||||||
type_to_string(trc, struct pubkey, &id));
|
if (peer->local) {
|
||||||
tal_free(peer);
|
status_trace("hand_back_peer %s: reconnected, dropping handback",
|
||||||
|
type_to_string(trc, struct pubkey, &rpeer->id));
|
||||||
|
|
||||||
|
close(rpeer->peer_fd);
|
||||||
|
tal_free(rpeer);
|
||||||
|
return daemon_conn_read_next(conn, &daemon->master);
|
||||||
}
|
}
|
||||||
|
|
||||||
status_trace("handle_peer %s: new peer",
|
status_trace("hand_back_peer %s: now local again",
|
||||||
type_to_string(trc, struct pubkey, &id));
|
type_to_string(trc, struct pubkey, &rpeer->id));
|
||||||
peer = new_peer(daemon, daemon, &id, &addr, &cs);
|
|
||||||
peer->gfeatures = tal_steal(peer, gfeatures);
|
|
||||||
peer->lfeatures = tal_steal(peer, lfeatures);
|
|
||||||
peer_finalized(peer);
|
|
||||||
|
|
||||||
if (tal_len(inner_msg))
|
/* Now we talk to peer directly again. */
|
||||||
msg_enqueue(&peer->local->peer_out, take(inner_msg));
|
daemon_conn_clear(peer->remote);
|
||||||
|
peer->remote = tal_free(peer->remote);
|
||||||
|
|
||||||
return io_recv_fd(conn, &peer->local->fd, new_peer_got_fd, peer);
|
peer->local = new_local_peer_state(peer, &rpeer->cs);
|
||||||
|
peer->local->fd = rpeer->peer_fd;
|
||||||
|
|
||||||
|
/* If they told us to send a message, queue it now */
|
||||||
|
if (tal_len(rpeer->inner_msg))
|
||||||
|
msg_enqueue(&peer->local->peer_out, take(rpeer->inner_msg));
|
||||||
|
tal_free(rpeer);
|
||||||
|
|
||||||
|
return new_peer_got_fd(conn, peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct io_plan *read_returning_gossipfd(struct io_conn *conn,
|
||||||
|
struct returning_peer *rpeer)
|
||||||
|
{
|
||||||
|
return io_recv_fd(conn, &rpeer->gossip_fd,
|
||||||
|
handle_returning_peer, rpeer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct io_plan *hand_back_peer(struct io_conn *conn,
|
||||||
|
struct daemon *daemon,
|
||||||
|
const u8 *msg)
|
||||||
|
{
|
||||||
|
struct returning_peer *rpeer = tal(daemon, struct returning_peer);
|
||||||
|
|
||||||
|
rpeer->daemon = daemon;
|
||||||
|
if (!fromwire_gossipctl_hand_back_peer(msg, msg, NULL,
|
||||||
|
&rpeer->id, &rpeer->cs,
|
||||||
|
&rpeer->inner_msg))
|
||||||
|
master_badmsg(WIRE_GOSSIPCTL_HAND_BACK_PEER, msg);
|
||||||
|
|
||||||
|
return io_recv_fd(conn, &rpeer->peer_fd,
|
||||||
|
read_returning_gossipfd, rpeer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct io_plan *release_peer(struct io_conn *conn, struct daemon *daemon,
|
static struct io_plan *release_peer(struct io_conn *conn, struct daemon *daemon,
|
||||||
@@ -1483,8 +1508,8 @@ static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master
|
|||||||
handle_forwarded_msg(conn, daemon, daemon->master.msg_in);
|
handle_forwarded_msg(conn, daemon, daemon->master.msg_in);
|
||||||
return daemon_conn_read_next(conn, &daemon->master);
|
return daemon_conn_read_next(conn, &daemon->master);
|
||||||
|
|
||||||
case WIRE_GOSSIPCTL_HANDLE_PEER:
|
case WIRE_GOSSIPCTL_HAND_BACK_PEER:
|
||||||
return handle_peer(conn, daemon, master->msg_in);
|
return hand_back_peer(conn, daemon, master->msg_in);
|
||||||
|
|
||||||
case WIRE_GOSSIPCTL_REACH_PEER:
|
case WIRE_GOSSIPCTL_REACH_PEER:
|
||||||
return reach_peer(conn, daemon, master->msg_in);
|
return reach_peer(conn, daemon, master->msg_in);
|
||||||
|
|||||||
@@ -64,17 +64,12 @@ gossipctl_release_peer_reply,,lfeatures,lflen*u8
|
|||||||
# Gossipd -> master: reply to gossip_release_peer if we couldn't find the peer.
|
# Gossipd -> master: reply to gossip_release_peer if we couldn't find the peer.
|
||||||
gossipctl_release_peer_replyfail,3204
|
gossipctl_release_peer_replyfail,3204
|
||||||
|
|
||||||
# Gossipd -> master: take over peer, with optional msg. (+peer fd)
|
# Gossipd -> master: take back peer, with optional msg. (+peer fd, +gossip fd)
|
||||||
gossipctl_handle_peer,3013
|
gossipctl_hand_back_peer,3013
|
||||||
gossipctl_handle_peer,,id,struct pubkey
|
gossipctl_hand_back_peer,,id,struct pubkey
|
||||||
gossipctl_handle_peer,,addr,struct wireaddr
|
gossipctl_hand_back_peer,,crypto_state,struct crypto_state
|
||||||
gossipctl_handle_peer,,crypto_state,struct crypto_state
|
gossipctl_hand_back_peer,,len,u16
|
||||||
gossipctl_handle_peer,,gflen,u16
|
gossipctl_hand_back_peer,,msg,len*u8
|
||||||
gossipctl_handle_peer,,gfeatures,gflen*u8
|
|
||||||
gossipctl_handle_peer,,lflen,u16
|
|
||||||
gossipctl_handle_peer,,lfeatures,lflen*u8
|
|
||||||
gossipctl_handle_peer,,len,u16
|
|
||||||
gossipctl_handle_peer,,msg,len*u8
|
|
||||||
|
|
||||||
# Pass JSON-RPC getnodes call through
|
# Pass JSON-RPC getnodes call through
|
||||||
gossip_getnodes_request,3005
|
gossip_getnodes_request,3005
|
||||||
|
|||||||
|
@@ -66,7 +66,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds)
|
|||||||
case WIRE_GOSSIP_RESOLVE_CHANNEL_REQUEST:
|
case WIRE_GOSSIP_RESOLVE_CHANNEL_REQUEST:
|
||||||
case WIRE_GOSSIP_FORWARDED_MSG:
|
case WIRE_GOSSIP_FORWARDED_MSG:
|
||||||
case WIRE_GOSSIPCTL_REACH_PEER:
|
case WIRE_GOSSIPCTL_REACH_PEER:
|
||||||
case WIRE_GOSSIPCTL_HANDLE_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:
|
||||||
case WIRE_GOSSIP_GET_UPDATE:
|
case WIRE_GOSSIP_GET_UPDATE:
|
||||||
|
|||||||
@@ -640,11 +640,10 @@ void peer_connected(struct lightningd *ld, const u8 *msg,
|
|||||||
|
|
||||||
return_to_gossipd:
|
return_to_gossipd:
|
||||||
/* Otherwise, we hand back to gossipd, to continue. */
|
/* Otherwise, we hand back to gossipd, to continue. */
|
||||||
msg = towire_gossipctl_handle_peer(msg, &id, &addr, &cs,
|
msg = towire_gossipctl_hand_back_peer(msg, &id, &cs, NULL);
|
||||||
gfeatures, lfeatures, NULL);
|
|
||||||
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);
|
||||||
close(gossip_fd);
|
subd_send_fd(ld->gossip, gossip_fd);
|
||||||
|
|
||||||
/* If we were waiting for connection, we succeeded. */
|
/* If we were waiting for connection, we succeeded. */
|
||||||
connect_succeeded(ld, &id);
|
connect_succeeded(ld, &id);
|
||||||
@@ -653,11 +652,10 @@ return_to_gossipd:
|
|||||||
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));
|
connect_failed(ld, &id, sanitize_error(msg, error, NULL));
|
||||||
msg = towire_gossipctl_handle_peer(msg, &id, &addr, &cs,
|
msg = towire_gossipctl_hand_back_peer(msg, &id, &cs, error);
|
||||||
gfeatures, lfeatures, 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);
|
||||||
close(gossip_fd);
|
subd_send_fd(ld->gossip, gossip_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void peer_sent_nongossip(struct lightningd *ld,
|
void peer_sent_nongossip(struct lightningd *ld,
|
||||||
@@ -704,11 +702,10 @@ 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(error, error, NULL));
|
connect_failed(ld, id, sanitize_error(error, error, NULL));
|
||||||
msg = towire_gossipctl_handle_peer(error, id, addr, cs,
|
msg = towire_gossipctl_hand_back_peer(ld, id, cs, error);
|
||||||
gfeatures, lfeatures, 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);
|
||||||
close(gossip_fd);
|
subd_send_fd(ld->gossip, gossip_fd);
|
||||||
tal_free(error);
|
tal_free(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2393,9 +2390,9 @@ static unsigned int opening_negotiation_failed(struct subd *openingd,
|
|||||||
u8 *err;
|
u8 *err;
|
||||||
const char *why;
|
const char *why;
|
||||||
|
|
||||||
/* We need the peer fd. */
|
/* We need the peer fd and gossip fd. */
|
||||||
if (tal_count(fds) == 0)
|
if (tal_count(fds) == 0)
|
||||||
return 1;
|
return 2;
|
||||||
|
|
||||||
if (!fromwire_opening_negotiation_failed(msg, msg, NULL, &cs, &err)) {
|
if (!fromwire_opening_negotiation_failed(msg, msg, NULL, &cs, &err)) {
|
||||||
peer_internal_error(peer,
|
peer_internal_error(peer,
|
||||||
@@ -2404,12 +2401,10 @@ static unsigned int opening_negotiation_failed(struct subd *openingd,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: Should we save addr in peer, or should gossipd remember it? */
|
msg = towire_gossipctl_hand_back_peer(msg, &peer->id, &cs, NULL);
|
||||||
msg = towire_gossipctl_handle_peer(msg, &peer->id, NULL, &cs,
|
|
||||||
peer->gfeatures, peer->lfeatures,
|
|
||||||
NULL);
|
|
||||||
subd_send_msg(openingd->ld->gossip, take(msg));
|
subd_send_msg(openingd->ld->gossip, take(msg));
|
||||||
subd_send_fd(openingd->ld->gossip, fds[0]);
|
subd_send_fd(openingd->ld->gossip, fds[0]);
|
||||||
|
subd_send_fd(openingd->ld->gossip, fds[1]);
|
||||||
|
|
||||||
why = tal_strndup(peer, (const char *)err, tal_len(err));
|
why = tal_strndup(peer, (const char *)err, tal_len(err));
|
||||||
log_unusual(peer->log, "Opening negotiation failed: %s", why);
|
log_unusual(peer->log, "Opening negotiation failed: %s", why);
|
||||||
|
|||||||
@@ -26,9 +26,6 @@ struct peer {
|
|||||||
/* ID of peer */
|
/* ID of peer */
|
||||||
struct pubkey id;
|
struct pubkey id;
|
||||||
|
|
||||||
/* Global and local features bitfields. */
|
|
||||||
const u8 *gfeatures, *lfeatures;
|
|
||||||
|
|
||||||
/* Error message (iff in error state) */
|
/* Error message (iff in error state) */
|
||||||
u8 *error;
|
u8 *error;
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ static void negotiation_failed(struct state *state, bool send_error,
|
|||||||
(const u8 *)errmsg);
|
(const u8 *)errmsg);
|
||||||
wire_sync_write(REQ_FD, msg);
|
wire_sync_write(REQ_FD, msg);
|
||||||
fdpass_send(REQ_FD, PEER_FD);
|
fdpass_send(REQ_FD, PEER_FD);
|
||||||
|
fdpass_send(REQ_FD, GOSSIP_FD);
|
||||||
|
|
||||||
tal_free(state);
|
tal_free(state);
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|||||||
@@ -221,8 +221,8 @@ class LightningDTests(BaseLightningDTests):
|
|||||||
|
|
||||||
assert ret['id'] == l2.info['id']
|
assert ret['id'] == l2.info['id']
|
||||||
|
|
||||||
l1.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER')
|
l1.daemon.wait_for_log('WIRE_GOSSIPCTL_HAND_BACK_PEER')
|
||||||
l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER')
|
l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HAND_BACK_PEER')
|
||||||
return l1,l2
|
return l1,l2
|
||||||
|
|
||||||
# Returns the short channel-id: <blocknum>:<txnum>:<outnum>
|
# Returns the short channel-id: <blocknum>:<txnum>:<outnum>
|
||||||
@@ -342,8 +342,8 @@ class LightningDTests(BaseLightningDTests):
|
|||||||
assert l2.rpc.getpeer(l1.info['id'])['state'] == 'GOSSIPING'
|
assert l2.rpc.getpeer(l1.info['id'])['state'] == 'GOSSIPING'
|
||||||
|
|
||||||
# Both gossipds will have them as new peers once handed back.
|
# Both gossipds will have them as new peers once handed back.
|
||||||
l1.daemon.wait_for_log('handle_peer {}: new peer'.format(l2.info['id']))
|
l1.daemon.wait_for_log('hand_back_peer {}: now local again'.format(l2.info['id']))
|
||||||
l2.daemon.wait_for_log('handle_peer {}: new peer'.format(l1.info['id']))
|
l2.daemon.wait_for_log('hand_back_peer {}: now local again'.format(l1.info['id']))
|
||||||
|
|
||||||
def test_balance(self):
|
def test_balance(self):
|
||||||
l1,l2 = self.connect()
|
l1,l2 = self.connect()
|
||||||
@@ -709,8 +709,8 @@ class LightningDTests(BaseLightningDTests):
|
|||||||
|
|
||||||
assert ret['id'] == l2.info['id']
|
assert ret['id'] == l2.info['id']
|
||||||
|
|
||||||
l1.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER')
|
l1.daemon.wait_for_log('WIRE_GOSSIPCTL_HAND_BACK_PEER')
|
||||||
l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER')
|
l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HAND_BACK_PEER')
|
||||||
|
|
||||||
addr = l1.rpc.newaddr()['address']
|
addr = l1.rpc.newaddr()['address']
|
||||||
txid = l1.bitcoin.rpc.sendtoaddress(addr, 10**6 / 10**8 + 0.01)
|
txid = l1.bitcoin.rpc.sendtoaddress(addr, 10**6 / 10**8 + 0.01)
|
||||||
@@ -1406,7 +1406,7 @@ class LightningDTests(BaseLightningDTests):
|
|||||||
|
|
||||||
assert ret['id'] == l3.info['id']
|
assert ret['id'] == l3.info['id']
|
||||||
|
|
||||||
l3.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER')
|
l3.daemon.wait_for_log('WIRE_GOSSIPCTL_HAND_BACK_PEER')
|
||||||
self.fund_channel(l1, l2, 10**6)
|
self.fund_channel(l1, l2, 10**6)
|
||||||
self.fund_channel(l2, l3, 10**6)
|
self.fund_channel(l2, l3, 10**6)
|
||||||
|
|
||||||
@@ -1497,14 +1497,14 @@ class LightningDTests(BaseLightningDTests):
|
|||||||
ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port'])
|
ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port'])
|
||||||
assert ret['id'] == l2.info['id']
|
assert ret['id'] == l2.info['id']
|
||||||
|
|
||||||
l1.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER')
|
l1.daemon.wait_for_log('WIRE_GOSSIPCTL_HAND_BACK_PEER')
|
||||||
l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER')
|
l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HAND_BACK_PEER')
|
||||||
|
|
||||||
ret = l2.rpc.connect(l3.info['id'], 'localhost', l3.info['port'])
|
ret = l2.rpc.connect(l3.info['id'], 'localhost', l3.info['port'])
|
||||||
assert ret['id'] == l3.info['id']
|
assert ret['id'] == l3.info['id']
|
||||||
|
|
||||||
l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER')
|
l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HAND_BACK_PEER')
|
||||||
l3.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER')
|
l3.daemon.wait_for_log('WIRE_GOSSIPCTL_HAND_BACK_PEER')
|
||||||
|
|
||||||
c1 = self.fund_channel(l1, l2, 10**6)
|
c1 = self.fund_channel(l1, l2, 10**6)
|
||||||
c2 = self.fund_channel(l2, l3, 10**6)
|
c2 = self.fund_channel(l2, l3, 10**6)
|
||||||
@@ -1596,14 +1596,14 @@ class LightningDTests(BaseLightningDTests):
|
|||||||
ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port'])
|
ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port'])
|
||||||
assert ret['id'] == l2.info['id']
|
assert ret['id'] == l2.info['id']
|
||||||
|
|
||||||
l1.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER')
|
l1.daemon.wait_for_log('WIRE_GOSSIPCTL_HAND_BACK_PEER')
|
||||||
l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER')
|
l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HAND_BACK_PEER')
|
||||||
|
|
||||||
ret = l2.rpc.connect(l3.info['id'], 'localhost', l3.info['port'])
|
ret = l2.rpc.connect(l3.info['id'], 'localhost', l3.info['port'])
|
||||||
assert ret['id'] == l3.info['id']
|
assert ret['id'] == l3.info['id']
|
||||||
|
|
||||||
l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER')
|
l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HAND_BACK_PEER')
|
||||||
l3.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER')
|
l3.daemon.wait_for_log('WIRE_GOSSIPCTL_HAND_BACK_PEER')
|
||||||
|
|
||||||
c1 = self.fund_channel(l1, l2, 10**6)
|
c1 = self.fund_channel(l1, l2, 10**6)
|
||||||
c2 = self.fund_channel(l2, l3, 10**6)
|
c2 = self.fund_channel(l2, l3, 10**6)
|
||||||
|
|||||||
Reference in New Issue
Block a user