Browse Source
Now the flow is much simpler from a lightningd POV: 1. If we want to connect to a peer, just send gossipd `gossipctl_reach_peer`. 2. Every new peer, gossipd hands up to lightningd, with global/local features and the peer fd and a gossip fd using `gossip_peer_connected` 3. If lightningd doesn't want it, it just hands the peerfd and global/local features back to gossipd using `gossipctl_handle_peer` 4. If a peer sends a non-gossip msg (eg `open_channel`) the gossipd sends it up using `gossip_peer_nongossip`. 5. If lightningd wants to fund a channel, it simply calls `release_channel`. Notes: * There's no more "unique_id": we use the peer id. * For the moment, we don't ask gossipd when we're told to list peers, so connected peers without a channel don't appear in the JSON getpeers API. * We add a `gossipctl_peer_addrhint` for the moment, so you can connect to a specific ip/port, but using other sources is a TODO. * We now (correctly) only give up on reaching a peer after we exchange init messages, which changes the test_disconnect case. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>ppa-0.6.1
committed by
Christian Decker
20 changed files with 1481 additions and 1695 deletions
File diff suppressed because it is too large
Can't render this file because it has a wrong number of fields in line 2.
|
@ -1,247 +0,0 @@ |
|||
/* Async dns helper. */ |
|||
#include "dns.h" |
|||
#include "lightningd.h" |
|||
#include "log.h" |
|||
#include "netaddr.h" |
|||
#include <assert.h> |
|||
#include <ccan/err/err.h> |
|||
#include <ccan/read_write_all/read_write_all.h> |
|||
#include <ccan/tal/str/str.h> |
|||
#include <ccan/tal/tal.h> |
|||
#include <errno.h> |
|||
#include <netdb.h> |
|||
#include <stdio.h> |
|||
#include <sys/socket.h> |
|||
#include <sys/types.h> |
|||
#include <sys/wait.h> |
|||
|
|||
struct dns_async { |
|||
struct lightningd *ld; |
|||
struct io_plan *(*init)(struct io_conn *, struct lightningd *, |
|||
const struct netaddr *, |
|||
void *); |
|||
void (*fail)(struct lightningd *, void *arg); |
|||
const char *name; |
|||
void *arg; |
|||
int pid; |
|||
size_t num_addresses; |
|||
struct netaddr *addresses; |
|||
}; |
|||
|
|||
/* This runs in the child */ |
|||
static void lookup_and_write(int fd, const char *name, const char *port) |
|||
{ |
|||
struct addrinfo *addr, *i; |
|||
struct netaddr *addresses; |
|||
size_t num; |
|||
struct addrinfo hints; |
|||
|
|||
/* We don't want UDP sockets (yet?) */ |
|||
memset(&hints, 0, sizeof(hints)); |
|||
hints.ai_family = AF_UNSPEC; |
|||
hints.ai_socktype = SOCK_STREAM; |
|||
if (getaddrinfo(name, port, &hints, &addr) != 0) |
|||
return; |
|||
|
|||
num = 0; |
|||
for (i = addr; i; i = i->ai_next) |
|||
num++; |
|||
|
|||
addresses = tal_arr(NULL, struct netaddr, num); |
|||
num = 0; |
|||
for (i = addr; i; i = i->ai_next) { |
|||
addresses[num].type = i->ai_socktype; |
|||
addresses[num].protocol = i->ai_protocol; |
|||
addresses[num].addrlen = i->ai_addrlen; |
|||
memset(&addresses[num].saddr, 0, sizeof(addresses[num].saddr)); |
|||
/* Let parent report this error. */ |
|||
if (i->ai_addrlen <= sizeof(addresses[num].saddr)) |
|||
memcpy(&addresses[num].saddr, i->ai_addr, i->ai_addrlen); |
|||
num++; |
|||
} |
|||
|
|||
if (!num) { |
|||
tal_free(addresses); |
|||
return; |
|||
} |
|||
|
|||
if (write_all(fd, &num, sizeof(num))) |
|||
write_all(fd, addresses, num * sizeof(addresses[0])); |
|||
tal_free(addresses); |
|||
} |
|||
|
|||
static struct io_plan *connected(struct io_conn *conn, struct dns_async *d) |
|||
{ |
|||
struct io_plan *plan; |
|||
|
|||
/* No longer need to try more connections via connect_failed. */ |
|||
io_set_finish(conn, NULL, NULL); |
|||
|
|||
plan = d->init(conn, d->ld, &d->addresses[-1], d->arg); |
|||
tal_free(d); |
|||
|
|||
return plan; |
|||
} |
|||
|
|||
static void try_connect_one(struct dns_async *d); |
|||
|
|||
/* If this connection failed, try connecting to another address. */ |
|||
static void connect_failed(struct io_conn *conn, struct dns_async *d) |
|||
{ |
|||
try_connect_one(d); |
|||
} |
|||
|
|||
static struct io_plan *init_conn(struct io_conn *conn, struct dns_async *d) |
|||
{ |
|||
struct addrinfo a; |
|||
|
|||
netaddr_to_addrinfo(&a, &d->addresses[0]); |
|||
|
|||
/* Consume that address. */ |
|||
d->addresses++; |
|||
d->num_addresses--; |
|||
|
|||
io_set_finish(conn, connect_failed, d); |
|||
|
|||
/* That new connection owns d */ |
|||
return io_connect(conn, &a, connected, d); |
|||
} |
|||
|
|||
static void try_connect_one(struct dns_async *d) |
|||
{ |
|||
int fd; |
|||
|
|||
while (d->num_addresses) { |
|||
const struct netaddr *a = &d->addresses[0]; |
|||
|
|||
/* Now we can warn if it's overlength */ |
|||
if (a->addrlen > sizeof(a->saddr)) { |
|||
log_broken(d->ld->log, |
|||
"DNS lookup gave overlength address for %s" |
|||
" for family %u, len=%u", |
|||
d->name, a->saddr.s.sa_family, a->addrlen); |
|||
} else { |
|||
/* Might not even be able to create eg. IPv6 sockets */ |
|||
fd = socket(a->saddr.s.sa_family, a->type, a->protocol); |
|||
if (fd >= 0) { |
|||
io_new_conn(d->ld, fd, init_conn, d); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
/* Consume that address. */ |
|||
d->addresses++; |
|||
d->num_addresses--; |
|||
} |
|||
|
|||
/* We're out of things to try. Fail. */ |
|||
d->fail(d->ld, d->arg); |
|||
tal_free(d); |
|||
} |
|||
|
|||
static struct io_plan *start_connecting(struct io_conn *conn, |
|||
struct dns_async *d) |
|||
{ |
|||
assert(d->num_addresses); |
|||
|
|||
/* OK, we've read all we want, child should exit. */ |
|||
waitpid(d->pid, NULL, 0); |
|||
|
|||
/* No need to call dns_lookup_failed now. */ |
|||
io_set_finish(conn, NULL, NULL); |
|||
|
|||
try_connect_one(d); |
|||
return io_close(conn); |
|||
} |
|||
|
|||
struct dns_async *multiaddress_connect_(struct lightningd *ld, |
|||
const struct netaddr *addresses, |
|||
struct io_plan *(*init)(struct io_conn *, |
|||
struct lightningd *, |
|||
const struct netaddr *, |
|||
void *arg), |
|||
void (*fail)(struct lightningd *, void *arg), |
|||
void *arg) |
|||
{ |
|||
struct dns_async *d = tal(ld, struct dns_async); |
|||
|
|||
d->ld = ld; |
|||
d->init = init; |
|||
d->fail = fail; |
|||
d->arg = arg; |
|||
d->name = "names from address list"; |
|||
d->num_addresses = tal_count(addresses); |
|||
d->addresses = tal_dup_arr(d, struct netaddr, addresses, |
|||
d->num_addresses, 0); |
|||
try_connect_one(d); |
|||
return d; |
|||
} |
|||
|
|||
static struct io_plan *read_addresses(struct io_conn *conn, struct dns_async *d) |
|||
{ |
|||
d->addresses = tal_arr(d, struct netaddr, d->num_addresses); |
|||
return io_read(conn, d->addresses, |
|||
d->num_addresses * sizeof(d->addresses[0]), |
|||
start_connecting, d); |
|||
} |
|||
|
|||
static struct io_plan *init_dns_conn(struct io_conn *conn, struct dns_async *d) |
|||
{ |
|||
return io_read(conn, &d->num_addresses, sizeof(d->num_addresses), |
|||
read_addresses, d); |
|||
} |
|||
|
|||
static void dns_lookup_failed(struct io_conn *conn, struct dns_async *d) |
|||
{ |
|||
waitpid(d->pid, NULL, 0); |
|||
d->fail(d->ld, d->arg); |
|||
tal_free(d); |
|||
} |
|||
|
|||
struct dns_async *dns_resolve_and_connect_(struct lightningd *ld, |
|||
const char *name, const char *port, |
|||
struct io_plan *(*init)(struct io_conn *, |
|||
struct lightningd *, |
|||
const struct netaddr *, |
|||
void *arg), |
|||
void (*fail)(struct lightningd *, void *arg), |
|||
void *arg) |
|||
{ |
|||
int pfds[2]; |
|||
struct dns_async *d = tal(ld, struct dns_async); |
|||
struct io_conn *conn; |
|||
|
|||
d->ld = ld; |
|||
d->init = init; |
|||
d->fail = fail; |
|||
d->arg = arg; |
|||
d->name = tal_fmt(d, "%s:%s", name, port); |
|||
|
|||
/* First fork child to get addresses. */ |
|||
if (pipe(pfds) != 0) { |
|||
log_unusual(ld->log, |
|||
"Creating pipes for dns lookup: %s", |
|||
strerror(errno)); |
|||
return NULL; |
|||
} |
|||
|
|||
fflush(stdout); |
|||
d->pid = fork(); |
|||
switch (d->pid) { |
|||
case -1: |
|||
log_unusual(ld->log, "forking for dns lookup: %s", |
|||
strerror(errno)); |
|||
close(pfds[0]); |
|||
close(pfds[1]); |
|||
return NULL; |
|||
case 0: |
|||
close(pfds[0]); |
|||
lookup_and_write(pfds[1], name, port); |
|||
exit(0); |
|||
} |
|||
|
|||
close(pfds[1]); |
|||
conn = io_new_conn(ld, pfds[0], init_dns_conn, d); |
|||
io_set_finish(conn, dns_lookup_failed, d); |
|||
return d; |
|||
} |
@ -1,53 +0,0 @@ |
|||
#ifndef LIGHTNING_LIGHTNINGD_DNS_H |
|||
#define LIGHTNING_LIGHTNINGD_DNS_H |
|||
#include "config.h" |
|||
#include <ccan/io/io.h> |
|||
#include <ccan/tal/tal.h> |
|||
#include <ccan/typesafe_cb/typesafe_cb.h> |
|||
#include <stdbool.h> |
|||
|
|||
struct lightningd; |
|||
struct netaddr; |
|||
|
|||
#define dns_resolve_and_connect(dstate, name, port, initfn, failfn, arg) \ |
|||
dns_resolve_and_connect_((dstate), (name), (port), \ |
|||
typesafe_cb_preargs(struct io_plan *, void *, \ |
|||
(initfn), (arg), \ |
|||
struct io_conn *, \ |
|||
struct lightningd *, \ |
|||
const struct netaddr *), \ |
|||
typesafe_cb_preargs(void, void *, (failfn), (arg), \ |
|||
struct lightningd *), \ |
|||
(arg)) |
|||
|
|||
struct dns_async *dns_resolve_and_connect_(struct lightningd *ld, |
|||
const char *name, const char *port, |
|||
struct io_plan *(*init)(struct io_conn *, |
|||
struct lightningd *, |
|||
const struct netaddr *, |
|||
void *arg), |
|||
void (*fail)(struct lightningd *, void *arg), |
|||
void *arg); |
|||
|
|||
/* Don't do lookup, just try to connect to these addresses. */ |
|||
#define multiaddress_connect(dstate, addresses, initfn, failfn, arg) \ |
|||
multiaddress_connect_((dstate), (addresses), \ |
|||
typesafe_cb_preargs(struct io_plan *, void *, \ |
|||
(initfn), (arg), \ |
|||
struct io_conn *, \ |
|||
struct lightningd *, \ |
|||
const struct netaddr *), \ |
|||
typesafe_cb_preargs(void, void *, (failfn), (arg), \ |
|||
struct lightningd *), \ |
|||
(arg)) |
|||
|
|||
struct dns_async *multiaddress_connect_(struct lightningd *ld, |
|||
const struct netaddr *addresses, |
|||
struct io_plan *(*init)(struct io_conn *, |
|||
struct lightningd *, |
|||
const struct netaddr *, |
|||
void *arg), |
|||
void (*fail)(struct lightningd *, void *arg), |
|||
void *arg); |
|||
|
|||
#endif /* LIGHTNING_LIGHTNINGD_DNS_H */ |
@ -1,283 +0,0 @@ |
|||
#include <ccan/array_size/array_size.h> |
|||
#include <ccan/fdpass/fdpass.h> |
|||
#include <ccan/tal/str/str.h> |
|||
#include <common/cryptomsg.h> |
|||
#include <errno.h> |
|||
#include <fcntl.h> |
|||
#include <handshaked/gen_handshake_wire.h> |
|||
#include <hsmd/gen_hsm_wire.h> |
|||
#include <lightningd/hsm_control.h> |
|||
#include <lightningd/jsonrpc.h> |
|||
#include <lightningd/lightningd.h> |
|||
#include <lightningd/log.h> |
|||
#include <lightningd/new_connection.h> |
|||
#include <lightningd/peer_control.h> |
|||
#include <lightningd/subd.h> |
|||
#include <unistd.h> |
|||
#include <wire/wire_sync.h> |
|||
|
|||
const u8 supported_local_features[] = {LOCALFEATURES_INITIAL_ROUTING_SYNC}; |
|||
const u8 supported_global_features[] = {0x00}; |
|||
|
|||
/* Before we have identified the peer, we just have a connection object. */ |
|||
struct connection { |
|||
/* Lightning daemon, for when we're handed through callbacks. */ |
|||
struct lightningd *ld; |
|||
|
|||
/* Where we connected to/from. */ |
|||
struct netaddr netaddr; |
|||
|
|||
/* Unique identifier for handshaked. */ |
|||
u64 unique_id; |
|||
|
|||
/* Json command which made us connect (if any) */ |
|||
struct command *cmd; |
|||
|
|||
/* If we are initiating, we known their id. Otherwise NULL. */ |
|||
struct pubkey *known_id; |
|||
}; |
|||
|
|||
static void connection_destroy(struct connection *c) |
|||
{ |
|||
/* FIXME: better diagnostics. */ |
|||
if (c->cmd) |
|||
command_fail(c->cmd, "Failed to connect to peer"); |
|||
} |
|||
|
|||
static void |
|||
PRINTF_FMT(3,4) connection_failed(struct connection *c, struct log *log, |
|||
const char *fmt, ...) |
|||
{ |
|||
const char *msg; |
|||
va_list ap; |
|||
|
|||
va_start(ap, fmt); |
|||
msg = tal_vfmt(c, fmt, ap); |
|||
va_end(ap); |
|||
log_info(log, "%s", msg); |
|||
if (c->cmd) { |
|||
command_fail(c->cmd, "%s", msg); |
|||
/* Don't fail in destructor, too. */ |
|||
c->cmd = NULL; |
|||
} |
|||
tal_free(c); |
|||
} |
|||
|
|||
struct connection *new_connection(const tal_t *ctx, |
|||
struct lightningd *ld, |
|||
struct command *cmd, |
|||
const struct pubkey *known_id) |
|||
{ |
|||
static u64 id_counter = 1; |
|||
struct connection *c = tal(ctx, struct connection); |
|||
|
|||
c->ld = ld; |
|||
c->unique_id = id_counter++; |
|||
c->cmd = cmd; |
|||
if (known_id) |
|||
c->known_id = tal_dup(c, struct pubkey, known_id); |
|||
else |
|||
c->known_id = NULL; |
|||
tal_add_destructor(c, connection_destroy); |
|||
|
|||
return c; |
|||
} |
|||
|
|||
/**
|
|||
* requires_unsupported_features - Check if we support what's being asked |
|||
* |
|||
* Given the features vector that the remote connection is expecting |
|||
* from us, we check to see if we support all even bit features, i.e., |
|||
* the required features. We do so by subtracting our own features in |
|||
* the provided positions and see if even bits remain. |
|||
* |
|||
* @bitmap: the features bitmap the peer is asking for |
|||
* @supportmap: what do we support |
|||
* @smlen: how long is our supportmap |
|||
*/ |
|||
static bool requires_unsupported_features(const u8 *bitmap, |
|||
const u8 *supportmap, |
|||
size_t smlen) |
|||
{ |
|||
size_t len = tal_count(bitmap); |
|||
u8 support; |
|||
for (size_t i=0; i<len; i++) { |
|||
/* Find matching bitmap byte in supportmap, 0x00 if none */ |
|||
if (len > smlen) { |
|||
support = 0x00; |
|||
} else { |
|||
support = supportmap[smlen-1]; |
|||
} |
|||
|
|||
/* Cancel out supported bits, check for even bits */ |
|||
if ((~support & bitmap[i]) & 0x55) |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
static bool handshake_succeeded(struct subd *handshaked, |
|||
const u8 *msg, const int *fds, |
|||
struct connection *c) |
|||
{ |
|||
struct crypto_state cs; |
|||
struct pubkey *id; |
|||
u8 *globalfeatures, *localfeatures; |
|||
|
|||
assert(tal_count(fds) == 1); |
|||
|
|||
/* FIXME: Look for peer duplicates! */ |
|||
|
|||
if (!c->known_id) { |
|||
id = tal(msg, struct pubkey); |
|||
if (!fromwire_handshake_responder_reply(c, msg, NULL, id, &cs, |
|||
&globalfeatures, |
|||
&localfeatures)) |
|||
goto err; |
|||
log_info(handshaked->log, "Peer in from %s", |
|||
type_to_string(ltmp, struct pubkey, id)); |
|||
} else { |
|||
id = c->known_id; |
|||
if (!fromwire_handshake_initiator_reply(c, msg, NULL, &cs, |
|||
&globalfeatures, |
|||
&localfeatures)) |
|||
goto err; |
|||
log_info(handshaked->log, "Peer out to %s", |
|||
type_to_string(ltmp, struct pubkey, id)); |
|||
} |
|||
|
|||
/* BOLT #1:
|
|||
* |
|||
* For unknown feature bits which are non-zero, the receiver |
|||
* MUST ignore the bit if the bit number is odd, and MUST fail |
|||
* the connection if the bit number is even. |
|||
*/ |
|||
if (requires_unsupported_features( |
|||
globalfeatures, supported_global_features, |
|||
ARRAY_SIZE(supported_global_features))) { |
|||
connection_failed(c, handshaked->log, |
|||
"peer %s: bad globalfeatures: %s", |
|||
type_to_string(c, struct pubkey, id), |
|||
tal_hex(msg, globalfeatures)); |
|||
return true; |
|||
} |
|||
|
|||
if (requires_unsupported_features( |
|||
localfeatures, supported_local_features, |
|||
ARRAY_SIZE(supported_local_features))) { |
|||
connection_failed(c, handshaked->log, |
|||
"peer %s: bad localfeatures: %s", |
|||
type_to_string(c, struct pubkey, id), |
|||
tal_hex(msg, localfeatures)); |
|||
return true; |
|||
} |
|||
|
|||
if (c->cmd) { |
|||
struct json_result *response; |
|||
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); |
|||
c->cmd = NULL; |
|||
} |
|||
|
|||
add_peer(handshaked->ld, c->unique_id, fds[0], id, &cs); |
|||
/* Now shut handshaked down (frees c as well) */ |
|||
return false; |
|||
|
|||
err: |
|||
log_broken(handshaked->log, "Malformed resp: %s", tal_hex(c, msg)); |
|||
close(fds[0]); |
|||
return false; |
|||
} |
|||
|
|||
/* Same path for connecting in vs connecting out. */ |
|||
static struct io_plan *hsm_then_handshake(struct io_conn *conn, |
|||
struct lightningd *ld, |
|||
struct connection *c) |
|||
{ |
|||
const tal_t *tmpctx = tal_tmpctx(conn); |
|||
int connfd = io_conn_fd(conn), hsmfd; |
|||
struct subd *handshaked; |
|||
u8 *msg; |
|||
|
|||
/* Get HSM fd for this peer. */ |
|||
msg = towire_hsmctl_hsmfd_ecdh(tmpctx, c->unique_id); |
|||
if (!wire_sync_write(ld->hsm_fd, msg)) |
|||
fatal("Could not write to HSM: %s", strerror(errno)); |
|||
|
|||
msg = hsm_sync_read(tmpctx, ld); |
|||
if (!fromwire_hsmctl_hsmfd_ecdh_fd_reply(msg, NULL)) |
|||
fatal("Malformed hsmfd response: %s", tal_hex(msg, msg)); |
|||
|
|||
hsmfd = fdpass_recv(ld->hsm_fd); |
|||
if (hsmfd < 0) |
|||
fatal("Could not read fd from HSM: %s", strerror(errno)); |
|||
|
|||
/* Make sure connection fd is blocking */ |
|||
io_fd_block(connfd, true); |
|||
|
|||
/* Give handshake daemon the hsm fd. */ |
|||
handshaked = new_subd(ld, |
|||
"lightning_handshaked", NULL, |
|||
handshake_wire_type_name, |
|||
NULL, NULL, NULL, |
|||
take(&hsmfd), take(&connfd), NULL); |
|||
if (!handshaked) { |
|||
log_unusual(ld->log, "Could not subdaemon handshake: %s", |
|||
strerror(errno)); |
|||
goto error; |
|||
} |
|||
|
|||
/* If handshake daemon fails, we just drop connection. */ |
|||
tal_steal(handshaked, c); |
|||
|
|||
if (c->known_id) { |
|||
msg = towire_handshake_initiator(tmpctx, &ld->id, c->known_id); |
|||
} else { |
|||
msg = towire_handshake_responder(tmpctx, &ld->id); |
|||
} |
|||
|
|||
/* Now hand peer request to the handshake daemon: hands it
|
|||
* back on success */ |
|||
subd_req(c, handshaked, take(msg), -1, 1, handshake_succeeded, c); |
|||
|
|||
tal_free(tmpctx); |
|||
|
|||
/* We don't need conn, we've passed fd to handshaked. */ |
|||
return io_close_taken_fd(conn); |
|||
|
|||
error: |
|||
close(hsmfd); |
|||
tal_free(tmpctx); |
|||
return io_close(conn); |
|||
} |
|||
|
|||
struct io_plan *connection_out(struct io_conn *conn, |
|||
struct lightningd *ld, |
|||
const struct netaddr *netaddr, |
|||
struct connection *c) |
|||
{ |
|||
c->netaddr = *netaddr; |
|||
return hsm_then_handshake(conn, ld, c); |
|||
} |
|||
|
|||
struct io_plan *connection_in(struct io_conn *conn, struct lightningd *ld) |
|||
{ |
|||
struct connection *c = new_connection(ld, ld, NULL, NULL); |
|||
|
|||
/* FIXME: Don't assume TCP here. */ |
|||
if (!netaddr_from_fd(io_conn_fd(conn), SOCK_STREAM, IPPROTO_TCP, |
|||
&c->netaddr)) { |
|||
log_unusual(ld->log, "Could not get address of incoming fd"); |
|||
return io_close(conn); |
|||
} |
|||
return hsm_then_handshake(conn, ld, c); |
|||
} |
|||
|
|||
const struct pubkey *connection_known_id(const struct connection *c) |
|||
{ |
|||
return c->known_id; |
|||
} |
@ -1,25 +0,0 @@ |
|||
#ifndef LIGHTNING_LIGHTNINGD_NEW_CONNECTION_H |
|||
#define LIGHTNING_LIGHTNINGD_NEW_CONNECTION_H |
|||
#include "config.h" |
|||
#include <stdbool.h> |
|||
|
|||
struct command; |
|||
struct io_conn; |
|||
struct lightningd; |
|||
struct netaddr; |
|||
struct pubkey; |
|||
|
|||
struct connection *new_connection(const tal_t *ctx, |
|||
struct lightningd *ld, |
|||
struct command *cmd, |
|||
const struct pubkey *known_id); |
|||
|
|||
struct io_plan *connection_out(struct io_conn *conn, |
|||
struct lightningd *dstate, |
|||
const struct netaddr *netaddr, |
|||
struct connection *c); |
|||
|
|||
struct io_plan *connection_in(struct io_conn *conn, struct lightningd *ld); |
|||
|
|||
const struct pubkey *connection_known_id(const struct connection *c); |
|||
#endif /* LIGHTNING_LIGHTNINGD_NEW_CONNECTION_H */ |
File diff suppressed because it is too large
Loading…
Reference in new issue