Files
lightning/lightningd/gossip_control.c
Rusty Russell 94711969f9 gossipd: hand out gossip_index to other daemons.
When gossipd sends a message, have a gossip_index.  When it gets back a
peer, the current gossip_index is included, so it can know exactly where
it's up to.

Most of this is mechanical plumbing through openingd, channeld and closingd,
even though openingd and closingd don't (currently) read gossip, so their
gossip_index will be unchanged.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2017-12-17 02:44:20 +00:00

321 lines
9.7 KiB
C

#include "gossip_control.h"
#include "lightningd.h"
#include "peer_control.h"
#include "subd.h"
#include <ccan/err/err.h>
#include <ccan/fdpass/fdpass.h>
#include <ccan/take/take.h>
#include <ccan/tal/str/str.h>
#include <common/type_to_string.h>
#include <common/utils.h>
#include <errno.h>
#include <gossipd/gen_gossip_wire.h>
#include <hsmd/capabilities.h>
#include <hsmd/gen_hsm_client_wire.h>
#include <inttypes.h>
#include <lightningd/gossip_msg.h>
#include <lightningd/hsm_control.h>
#include <lightningd/jsonrpc.h>
#include <lightningd/log.h>
#include <wire/gen_peer_wire.h>
#include <wire/wire_sync.h>
static void peer_nongossip(struct subd *gossip, const u8 *msg,
int peer_fd, int gossip_fd)
{
struct pubkey id;
struct crypto_state cs;
struct wireaddr addr;
u8 *gfeatures, *lfeatures, *in_pkt;
u64 gossip_index;
if (!fromwire_gossip_peer_nongossip(msg, msg, NULL,
&id, &addr, &cs, &gossip_index,
&gfeatures,
&lfeatures,
&in_pkt))
fatal("Gossip gave bad GOSSIP_PEER_NONGOSSIP message %s",
tal_hex(msg, msg));
/* We already checked the features when it first connected. */
if (unsupported_features(gfeatures, lfeatures)) {
log_unusual(gossip->log,
"Gossip gave unsupported features %s/%s",
tal_hex(msg, gfeatures),
tal_hex(msg, lfeatures));
close(peer_fd);
close(gossip_fd);
return;
}
peer_sent_nongossip(gossip->ld, &id, &addr, &cs, gossip_index,
gfeatures, lfeatures,
peer_fd, gossip_fd, in_pkt);
}
static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds)
{
enum gossip_wire_type t = fromwire_peektype(msg);
switch (t) {
/* These are messages we send, not them. */
case WIRE_GOSSIPCTL_INIT:
case WIRE_GOSSIP_GETNODES_REQUEST:
case WIRE_GOSSIP_GETROUTE_REQUEST:
case WIRE_GOSSIP_GETCHANNELS_REQUEST:
case WIRE_GOSSIP_GETPEERS_REQUEST:
case WIRE_GOSSIP_PING:
case WIRE_GOSSIP_RESOLVE_CHANNEL_REQUEST:
case WIRE_GOSSIP_FORWARDED_MSG:
case WIRE_GOSSIPCTL_REACH_PEER:
case WIRE_GOSSIPCTL_HAND_BACK_PEER:
case WIRE_GOSSIPCTL_RELEASE_PEER:
case WIRE_GOSSIPCTL_PEER_ADDRHINT:
case WIRE_GOSSIP_GET_UPDATE:
case WIRE_GOSSIP_SEND_GOSSIP:
/* This is a reply, so never gets through to here. */
case WIRE_GOSSIP_GET_UPDATE_REPLY:
case WIRE_GOSSIP_GETNODES_REPLY:
case WIRE_GOSSIP_GETROUTE_REPLY:
case WIRE_GOSSIP_GETCHANNELS_REPLY:
case WIRE_GOSSIP_GETPEERS_REPLY:
case WIRE_GOSSIP_PING_REPLY:
case WIRE_GOSSIP_RESOLVE_CHANNEL_REPLY:
case WIRE_GOSSIPCTL_RELEASE_PEER_REPLY:
case WIRE_GOSSIPCTL_RELEASE_PEER_REPLYFAIL:
break;
case WIRE_GOSSIP_PEER_CONNECTED:
if (tal_count(fds) != 2)
return 2;
peer_connected(gossip->ld, msg, fds[0], fds[1]);
break;
case WIRE_GOSSIP_PEER_NONGOSSIP:
if (tal_count(fds) != 2)
return 2;
peer_nongossip(gossip, msg, fds[0], fds[1]);
break;
}
return 0;
}
/* Create the `gossipd` subdaemon and send the initialization
* message */
void gossip_init(struct lightningd *ld)
{
tal_t *tmpctx = tal_tmpctx(ld);
u8 *msg;
int hsmfd;
u64 capabilities = HSM_CAP_ECDH | HSM_CAP_SIGN_GOSSIP;
msg = towire_hsm_client_hsmfd(tmpctx, &ld->id, capabilities);
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_hsm_client_hsmfd_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));
ld->gossip = new_global_subd(ld, "lightning_gossipd",
gossip_wire_type_name, gossip_msg,
take(&hsmfd), NULL);
if (!ld->gossip)
err(1, "Could not subdaemon gossip");
msg = towire_gossipctl_init(tmpctx, ld->broadcast_interval,
&get_chainparams(ld)->genesis_blockhash,
&ld->id, ld->portnum,
get_supported_global_features(tmpctx),
get_supported_local_features(tmpctx),
ld->wireaddrs, ld->rgb, ld->alias);
subd_send_msg(ld->gossip, msg);
tal_free(tmpctx);
}
static void json_getnodes_reply(struct subd *gossip, const u8 *reply,
const int *fds,
struct command *cmd)
{
struct gossip_getnodes_entry *nodes;
struct json_result *response = new_json_result(cmd);
size_t i, j;
if (!fromwire_gossip_getnodes_reply(reply, reply, NULL, &nodes)) {
command_fail(cmd, "Malformed gossip_getnodes response");
return;
}
json_object_start(response, NULL);
json_array_start(response, "nodes");
for (i = 0; i < tal_count(nodes); i++) {
json_object_start(response, NULL);
json_add_pubkey(response, "nodeid", &nodes[i].nodeid);
json_array_start(response, "addresses");
for (j=0; j<tal_count(nodes[i].addresses); j++) {
json_add_address(response, NULL, &nodes[i].addresses[j]);
}
json_array_end(response);
json_object_end(response);
}
json_array_end(response);
json_object_end(response);
command_success(cmd, response);
}
static void json_getnodes(struct command *cmd, const char *buffer,
const jsmntok_t *params)
{
u8 *req = towire_gossip_getnodes_request(cmd);
subd_req(cmd, cmd->ld->gossip, req, -1, 0, json_getnodes_reply, cmd);
}
static const struct json_command getnodes_command = {
"getnodes", json_getnodes, "Retrieve all nodes in our local network view",
"Returns a list of all nodes that we know about"};
AUTODATA(json_command, &getnodes_command);
static void json_getroute_reply(struct subd *gossip, const u8 *reply, const int *fds,
struct command *cmd)
{
struct json_result *response;
struct route_hop *hops;
size_t i;
fromwire_gossip_getroute_reply(reply, reply, NULL, &hops);
if (tal_count(hops) == 0) {
command_fail(cmd, "Could not find a route");
return;
}
response = new_json_result(cmd);
json_object_start(response, NULL);
json_array_start(response, "route");
for (i = 0; i < tal_count(hops); i++) {
json_object_start(response, NULL);
json_add_pubkey(response, "id", &hops[i].nodeid);
json_add_short_channel_id(response, "channel",
&hops[i].channel_id);
json_add_u64(response, "msatoshi", hops[i].amount);
json_add_num(response, "delay", hops[i].delay);
json_object_end(response);
}
json_array_end(response);
json_object_end(response);
command_success(cmd, response);
}
static void json_getroute(struct command *cmd, const char *buffer, const jsmntok_t *params)
{
struct pubkey id;
jsmntok_t *idtok, *msatoshitok, *riskfactortok, *cltvtok;
u64 msatoshi;
unsigned cltv = 9;
double riskfactor;
struct lightningd *ld = cmd->ld;
if (!json_get_params(buffer, params,
"id", &idtok,
"msatoshi", &msatoshitok,
"riskfactor", &riskfactortok,
"?cltv", &cltvtok,
NULL)) {
command_fail(cmd, "Need id, msatoshi and riskfactor");
return;
}
if (!json_tok_pubkey(buffer, idtok, &id)) {
command_fail(cmd, "Invalid id");
return;
}
if (cltvtok && !json_tok_number(buffer, cltvtok, &cltv)) {
command_fail(cmd, "Invalid cltv");
return;
}
if (!json_tok_u64(buffer, msatoshitok, &msatoshi)) {
command_fail(cmd, "'%.*s' is not a valid number",
(int)(msatoshitok->end - msatoshitok->start),
buffer + msatoshitok->start);
return;
}
if (!json_tok_double(buffer, riskfactortok, &riskfactor)) {
command_fail(cmd, "'%.*s' is not a valid double",
(int)(riskfactortok->end - riskfactortok->start),
buffer + riskfactortok->start);
return;
}
u8 *req = towire_gossip_getroute_request(cmd, &ld->id, &id, msatoshi, riskfactor*1000, cltv);
subd_req(ld->gossip, ld->gossip, req, -1, 0, json_getroute_reply, cmd);
}
static const struct json_command getroute_command = {
"getroute", json_getroute,
"Return route to {id} for {msatoshi}, using {riskfactor} and optional {cltv} (default 9)",
"Returns a {route} array of {id} {msatoshi} {delay}: msatoshi and delay (in blocks) is cumulative."
};
AUTODATA(json_command, &getroute_command);
/* Called upon receiving a getchannels_reply from `gossipd` */
static void json_getchannels_reply(struct subd *gossip, const u8 *reply,
const int *fds, struct command *cmd)
{
size_t i;
struct gossip_getchannels_entry *entries;
struct json_result *response = new_json_result(cmd);
struct short_channel_id *scid;
if (!fromwire_gossip_getchannels_reply(reply, reply, NULL, &entries)) {
command_fail(cmd, "Invalid reply from gossipd");
return;
}
json_object_start(response, NULL);
json_array_start(response, "channels");
for (i = 0; i < tal_count(entries); i++) {
scid = &entries[i].short_channel_id;
json_object_start(response, NULL);
json_add_pubkey(response, "source", &entries[i].source);
json_add_pubkey(response, "destination",
&entries[i].destination);
json_add_string(response, "short_id",
tal_fmt(reply, "%d:%d:%d/%d", scid->blocknum,
scid->txnum, scid->outnum,
entries[i].flags & 0x1));
json_add_num(response, "flags", entries[i].flags);
json_add_bool(response, "active", entries[i].active);
if (entries[i].last_update_timestamp >= 0) {
json_add_num(response, "last_update",
entries[i].last_update_timestamp);
json_add_num(response, "base_fee_millisatoshi",
entries[i].base_fee_msat);
json_add_num(response, "fee_per_millionth",
entries[i].fee_per_millionth);
json_add_num(response, "delay", entries[i].delay);
}
json_object_end(response);
}
json_array_end(response);
json_object_end(response);
command_success(cmd, response);
}
static void json_getchannels(struct command *cmd, const char *buffer,
const jsmntok_t *params)
{
u8 *req = towire_gossip_getchannels_request(cmd);
subd_req(cmd->ld->gossip, cmd->ld->gossip,
req, -1, 0, json_getchannels_reply, cmd);
}
static const struct json_command getchannels_command = {
"getchannels", json_getchannels, "List all known channels.",
"Returns a 'channels' array with all known channels including their fees."};
AUTODATA(json_command, &getchannels_command);