Browse Source

gossipd: make more compact getchannels entries.

We can save significant space by combining both sides: so much that we
can reduce the WIRE_LEN_LIMIT to something sane again.

MCP results from 5 runs, min-max(mean +/- stddev):
	store_load_msec:34467-36764(35517.8+/-7.7e+02)
	vsz_kb:2637488
	store_rewrite_sec:35.310000-36.580000(35.816+/-0.44)
	listnodes_sec:1.140000-2.780000(1.596+/-0.6)
	listchannels_sec:55.390000-58.110000(56.998+/-0.99)
	routing_sec:30.330000-30.920000(30.642+/-0.19)
	peer_write_all_sec:50.640000-53.360000(51.822+/-0.91)

MCP notable changes from previous patch (>1 stddev):
	-store_rewrite_sec:34.720000-35.130000(34.94+/-0.14)
	+store_rewrite_sec:35.310000-36.580000(35.816+/-0.44)

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
pr-2587
Rusty Russell 6 years ago
committed by neil saitug
parent
commit
0b484b111e
  1. 93
      gossipd/gossipd.c
  2. 62
      lightningd/gossip_control.c
  3. 74
      lightningd/gossip_msg.c
  4. 22
      lightningd/gossip_msg.h
  5. 1
      tools/generate-wire.py
  6. 5
      wire/wire_io.h

93
gossipd/gossipd.c

@ -1947,55 +1947,62 @@ static struct io_plan *getroute_req(struct io_conn *conn, struct daemon *daemon,
* marshalling the channel information for all channels into an array of * marshalling the channel information for all channels into an array of
* gossip_getchannels_entry, which lightningd converts to JSON. Each channel * gossip_getchannels_entry, which lightningd converts to JSON. Each channel
* is represented by two half_chan; one in each direction. * is represented by two half_chan; one in each direction.
*
* FIXME: I run a lightning node permanently under valgrind, and Christian ran
* `listchannels` on it. After about 15 minutes I simply rebooted. There's
* been some optimization since then, but blocking gossipd to marshall all the
* channels will become in issue in future, I expect. We may even hit the
* 2^24 internal message limit.
*/ */
static void append_half_channel(struct gossip_getchannels_entry **entries, static struct gossip_halfchannel_entry *hc_entry(const tal_t *ctx,
const struct chan *chan, const struct chan *chan,
int idx) int idx)
{ {
const struct half_chan *c = &chan->half[idx];
struct gossip_getchannels_entry e;
/* If we've never seen a channel_update for this direction... */
if (!is_halfchan_defined(c))
return;
/* Our 'struct chan' contains two nodes: they are in pubkey_cmp order /* Our 'struct chan' contains two nodes: they are in pubkey_cmp order
* (ie. chan->nodes[0] is the lesser pubkey) and this is the same as * (ie. chan->nodes[0] is the lesser pubkey) and this is the same as
* the direction bit in `channel_update`s `channel_flags`. * the direction bit in `channel_update`s `channel_flags`.
* *
* The halfchans are arranged so that half[0] src == nodes[0], and we * The halfchans are arranged so that half[0] src == nodes[0], and we
* use that here. We also avoid using libsecp256k1 to translate the * use that here. */
* pubkeys to DER and back: that proves quite expensive, and we assume const struct half_chan *c = &chan->half[idx];
* we're on the same architecture as lightningd, so we just send them struct gossip_halfchannel_entry *e;
* raw in this case. */
e.source = chan->nodes[idx]->id;
e.destination = chan->nodes[!idx]->id;
e.sat = chan->sat;
e.channel_flags = c->channel_flags;
e.message_flags = c->message_flags;
e.local_disabled = chan->local_disabled;
e.public = is_chan_public(chan);
e.short_channel_id = chan->scid;
e.last_update_timestamp = c->last_timestamp;
e.base_fee_msat = c->base_fee;
e.fee_per_millionth = c->proportional_fee;
e.delay = c->delay;
tal_arr_expand(entries, e); /* If we've never seen a channel_update for this direction... */
if (!is_halfchan_defined(c))
return NULL;
e = tal(ctx, struct gossip_halfchannel_entry);
e->channel_flags = c->channel_flags;
e->message_flags = c->message_flags;
e->last_update_timestamp = c->last_timestamp;
e->base_fee_msat = c->base_fee;
e->fee_per_millionth = c->proportional_fee;
e->delay = c->delay;
return e;
} }
/*~ Marshal (possibly) both channel directions into entries */ /*~ Marshal (possibly) both channel directions into entries. */
static void append_channel(struct gossip_getchannels_entry **entries, static void append_channel(const struct gossip_getchannels_entry ***entries,
const struct chan *chan) const struct chan *chan,
{ const struct node_id *srcfilter)
append_half_channel(entries, chan, 0); {
append_half_channel(entries, chan, 1); struct gossip_getchannels_entry *e = tal(*entries, struct gossip_getchannels_entry);
e->node[0] = chan->nodes[0]->id;
e->node[1] = chan->nodes[1]->id;
e->sat = chan->sat;
e->local_disabled = chan->local_disabled;
e->public = is_chan_public(chan);
e->short_channel_id = chan->scid;
if (!srcfilter || node_id_eq(&e->node[0], srcfilter))
e->e[0] = hc_entry(*entries, chan, 0);
else
e->e[0] = NULL;
if (!srcfilter || node_id_eq(&e->node[1], srcfilter))
e->e[1] = hc_entry(*entries, chan, 1);
else
e->e[1] = NULL;
/* We choose not to tell lightningd about channels with no updates,
* as they're unusable and can't be represented in the listchannels
* JSON output we use anyway. */
if (e->e[0] || e->e[1])
tal_arr_expand(entries, e);
} }
/*~ This is where lightningd asks for all channels we know about. */ /*~ This is where lightningd asks for all channels we know about. */
@ -2004,7 +2011,7 @@ static struct io_plan *getchannels_req(struct io_conn *conn,
const u8 *msg) const u8 *msg)
{ {
u8 *out; u8 *out;
struct gossip_getchannels_entry *entries; const struct gossip_getchannels_entry **entries;
struct chan *chan; struct chan *chan;
struct short_channel_id *scid; struct short_channel_id *scid;
struct node_id *source; struct node_id *source;
@ -2013,12 +2020,12 @@ static struct io_plan *getchannels_req(struct io_conn *conn,
if (!fromwire_gossip_getchannels_request(msg, msg, &scid, &source)) if (!fromwire_gossip_getchannels_request(msg, msg, &scid, &source))
master_badmsg(WIRE_GOSSIP_GETCHANNELS_REQUEST, msg); master_badmsg(WIRE_GOSSIP_GETCHANNELS_REQUEST, msg);
entries = tal_arr(tmpctx, struct gossip_getchannels_entry, 0); entries = tal_arr(tmpctx, const struct gossip_getchannels_entry *, 0);
/* They can ask about a particular channel by short_channel_id */ /* They can ask about a particular channel by short_channel_id */
if (scid) { if (scid) {
chan = get_channel(daemon->rstate, scid); chan = get_channel(daemon->rstate, scid);
if (chan) if (chan)
append_channel(&entries, chan); append_channel(&entries, chan, NULL);
} else if (source) { } else if (source) {
struct node *s = get_node(daemon->rstate, source); struct node *s = get_node(daemon->rstate, source);
if (s) { if (s) {
@ -2026,9 +2033,7 @@ static struct io_plan *getchannels_req(struct io_conn *conn,
struct chan *c; struct chan *c;
for (c = first_chan(s, &i); c; c = next_chan(s, &i)) { for (c = first_chan(s, &i); c; c = next_chan(s, &i)) {
append_half_channel(&entries, append_channel(&entries, c, source);
c,
!half_chan_to(s, c));
} }
} }
} else { } else {
@ -2039,7 +2044,7 @@ static struct io_plan *getchannels_req(struct io_conn *conn,
for (chan = uintmap_first(&daemon->rstate->chanmap, &idx); for (chan = uintmap_first(&daemon->rstate->chanmap, &idx);
chan; chan;
chan = uintmap_after(&daemon->rstate->chanmap, &idx)) { chan = uintmap_after(&daemon->rstate->chanmap, &idx)) {
append_channel(&entries, chan); append_channel(&entries, chan, NULL);
} }
} }

62
lightningd/gossip_control.c

@ -367,12 +367,43 @@ static const struct json_command getroute_command = {
}; };
AUTODATA(json_command, &getroute_command); AUTODATA(json_command, &getroute_command);
static void json_add_halfchan(struct json_stream *response,
const struct gossip_getchannels_entry *e,
int idx)
{
const struct gossip_halfchannel_entry *he = e->e[idx];
if (!he)
return;
json_object_start(response, NULL);
json_add_node_id(response, "source", &e->node[idx]);
json_add_node_id(response, "destination", &e->node[!idx]);
json_add_short_channel_id(response, "short_channel_id",
&e->short_channel_id);
json_add_bool(response, "public", e->public);
json_add_amount_sat(response, e->sat,
"satoshis", "amount_msat");
json_add_num(response, "message_flags", he->message_flags);
json_add_num(response, "channel_flags", he->channel_flags);
/* Prior to spec v0891374d47ddffa64c5a2e6ad151247e3d6b7a59, these two were a single u16 field */
if (deprecated_apis)
json_add_num(response, "flags", ((u16)he->message_flags << 8) | he->channel_flags);
json_add_bool(response, "active",
!(he->channel_flags & ROUTING_FLAGS_DISABLED)
&& !e->local_disabled);
json_add_num(response, "last_update", he->last_update_timestamp);
json_add_num(response, "base_fee_millisatoshi", he->base_fee_msat);
json_add_num(response, "fee_per_millionth", he->fee_per_millionth);
json_add_num(response, "delay", he->delay);
json_object_end(response);
}
/* Called upon receiving a getchannels_reply from `gossipd` */ /* Called upon receiving a getchannels_reply from `gossipd` */
static void json_listchannels_reply(struct subd *gossip UNUSED, const u8 *reply, static void json_listchannels_reply(struct subd *gossip UNUSED, const u8 *reply,
const int *fds UNUSED, struct command *cmd) const int *fds UNUSED, struct command *cmd)
{ {
size_t i; size_t i;
struct gossip_getchannels_entry *entries; struct gossip_getchannels_entry **entries;
struct json_stream *response; struct json_stream *response;
if (!fromwire_gossip_getchannels_reply(reply, reply, &entries)) { if (!fromwire_gossip_getchannels_reply(reply, reply, &entries)) {
@ -385,33 +416,8 @@ static void json_listchannels_reply(struct subd *gossip UNUSED, const u8 *reply,
json_object_start(response, NULL); json_object_start(response, NULL);
json_array_start(response, "channels"); json_array_start(response, "channels");
for (i = 0; i < tal_count(entries); i++) { for (i = 0; i < tal_count(entries); i++) {
json_object_start(response, NULL); json_add_halfchan(response, entries[i], 0);
json_add_node_id(response, "source", json_add_halfchan(response, entries[i], 1);
&entries[i].source);
json_add_node_id(response, "destination",
&entries[i].destination);
json_add_string(response, "short_channel_id",
type_to_string(reply, struct short_channel_id,
&entries[i].short_channel_id));
json_add_bool(response, "public", entries[i].public);
json_add_amount_sat(response, entries[i].sat,
"satoshis", "amount_msat");
json_add_num(response, "message_flags", entries[i].message_flags);
json_add_num(response, "channel_flags", entries[i].channel_flags);
/* Prior to spec v0891374d47ddffa64c5a2e6ad151247e3d6b7a59, these two were a single u16 field */
if (deprecated_apis)
json_add_num(response, "flags", ((u16)entries[i].message_flags << 8) | entries[i].channel_flags);
json_add_bool(response, "active",
!(entries[i].channel_flags & ROUTING_FLAGS_DISABLED)
&& !entries[i].local_disabled);
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_array_end(response);
json_object_end(response); json_object_end(response);

74
lightningd/gossip_msg.c

@ -96,38 +96,76 @@ void towire_route_info(u8 **pptr, const struct route_info *entry)
towire_u16(pptr, entry->cltv_expiry_delta); towire_u16(pptr, entry->cltv_expiry_delta);
} }
void fromwire_gossip_getchannels_entry(const u8 **pptr, size_t *max, static void fromwire_gossip_halfchannel_entry(const u8 **pptr, size_t *max,
struct gossip_getchannels_entry *entry) struct gossip_halfchannel_entry *entry)
{ {
fromwire_short_channel_id(pptr, max, &entry->short_channel_id);
fromwire_node_id(pptr, max, &entry->source);
fromwire_node_id(pptr, max, &entry->destination);
entry->sat = fromwire_amount_sat(pptr, max);
entry->message_flags = fromwire_u8(pptr, max); entry->message_flags = fromwire_u8(pptr, max);
entry->channel_flags = fromwire_u8(pptr, max); entry->channel_flags = fromwire_u8(pptr, max);
entry->public = fromwire_bool(pptr, max);
entry->local_disabled = fromwire_bool(pptr, max);
entry->last_update_timestamp = fromwire_u32(pptr, max); entry->last_update_timestamp = fromwire_u32(pptr, max);
entry->delay = fromwire_u32(pptr, max);
entry->base_fee_msat = fromwire_u32(pptr, max); entry->base_fee_msat = fromwire_u32(pptr, max);
entry->fee_per_millionth = fromwire_u32(pptr, max); entry->fee_per_millionth = fromwire_u32(pptr, max);
entry->delay = fromwire_u32(pptr, max);
} }
void towire_gossip_getchannels_entry(u8 **pptr, struct gossip_getchannels_entry *
const struct gossip_getchannels_entry *entry) fromwire_gossip_getchannels_entry(const tal_t *ctx,
const u8 **pptr, size_t *max)
{
struct gossip_getchannels_entry *entry;
entry= tal(ctx, struct gossip_getchannels_entry);
fromwire_node_id(pptr, max, &entry->node[0]);
fromwire_node_id(pptr, max, &entry->node[1]);
entry->sat = fromwire_amount_sat(pptr, max);
fromwire_short_channel_id(pptr, max, &entry->short_channel_id);
entry->public = fromwire_bool(pptr, max);
entry->local_disabled = fromwire_bool(pptr, max);
if (fromwire_bool(pptr, max)) {
entry->e[0] = tal(entry, struct gossip_halfchannel_entry);
fromwire_gossip_halfchannel_entry(pptr, max, entry->e[0]);
} else
entry->e[0] = NULL;
if (fromwire_bool(pptr, max)) {
entry->e[1] = tal(entry, struct gossip_halfchannel_entry);
fromwire_gossip_halfchannel_entry(pptr, max, entry->e[1]);
} else
entry->e[1] = NULL;
return entry;
}
static void towire_gossip_halfchannel_entry(u8 **pptr,
const struct gossip_halfchannel_entry *entry)
{ {
towire_short_channel_id(pptr, &entry->short_channel_id);
towire_node_id(pptr, &entry->source);
towire_node_id(pptr, &entry->destination);
towire_amount_sat(pptr, entry->sat);
towire_u8(pptr, entry->message_flags); towire_u8(pptr, entry->message_flags);
towire_u8(pptr, entry->channel_flags); towire_u8(pptr, entry->channel_flags);
towire_bool(pptr, entry->public);
towire_bool(pptr, entry->local_disabled);
towire_u32(pptr, entry->last_update_timestamp); towire_u32(pptr, entry->last_update_timestamp);
towire_u32(pptr, entry->delay);
towire_u32(pptr, entry->base_fee_msat); towire_u32(pptr, entry->base_fee_msat);
towire_u32(pptr, entry->fee_per_millionth); towire_u32(pptr, entry->fee_per_millionth);
towire_u32(pptr, entry->delay); }
void towire_gossip_getchannels_entry(u8 **pptr,
const struct gossip_getchannels_entry *entry)
{
towire_node_id(pptr, &entry->node[0]);
towire_node_id(pptr, &entry->node[1]);
towire_amount_sat(pptr, entry->sat);
towire_short_channel_id(pptr, &entry->short_channel_id);
towire_bool(pptr, entry->public);
towire_bool(pptr, entry->local_disabled);
if (entry->e[0]) {
towire_bool(pptr, true);
towire_gossip_halfchannel_entry(pptr, entry->e[0]);
} else
towire_bool(pptr, false);
if (entry->e[1]) {
towire_bool(pptr, true);
towire_gossip_halfchannel_entry(pptr, entry->e[1]);
} else
towire_bool(pptr, false);
} }
struct peer_features * struct peer_features *

22
lightningd/gossip_msg.h

@ -20,20 +20,25 @@ struct gossip_getnodes_entry {
u8 color[3]; u8 color[3];
}; };
struct gossip_getchannels_entry { struct gossip_halfchannel_entry {
struct node_id source, destination;
struct amount_sat sat;
struct short_channel_id short_channel_id;
u8 message_flags; u8 message_flags;
u8 channel_flags; u8 channel_flags;
bool public;
bool local_disabled;
u32 last_update_timestamp; u32 last_update_timestamp;
u32 delay; u32 delay;
u32 base_fee_msat; u32 base_fee_msat;
u32 fee_per_millionth; u32 fee_per_millionth;
}; };
struct gossip_getchannels_entry {
struct node_id node[2];
struct amount_sat sat;
struct short_channel_id short_channel_id;
bool public;
bool local_disabled;
/* NULL if we haven't received an update */
struct gossip_halfchannel_entry *e[2];
};
struct gossip_getnodes_entry * struct gossip_getnodes_entry *
fromwire_gossip_getnodes_entry(const tal_t *ctx, const u8 **pptr, size_t *max); fromwire_gossip_getnodes_entry(const tal_t *ctx, const u8 **pptr, size_t *max);
void towire_gossip_getnodes_entry(u8 **pptr, void towire_gossip_getnodes_entry(u8 **pptr,
@ -49,8 +54,9 @@ void towire_route_hop(u8 **pprt, const struct route_hop *entry);
void fromwire_route_info(const u8 **pprt, size_t *max, struct route_info *entry); void fromwire_route_info(const u8 **pprt, size_t *max, struct route_info *entry);
void towire_route_info(u8 **pprt, const struct route_info *entry); void towire_route_info(u8 **pprt, const struct route_info *entry);
void fromwire_gossip_getchannels_entry(const u8 **pptr, size_t *max, struct gossip_getchannels_entry *
struct gossip_getchannels_entry *entry); fromwire_gossip_getchannels_entry(const tal_t *ctx,
const u8 **pptr, size_t *max);
void towire_gossip_getchannels_entry( void towire_gossip_getchannels_entry(
u8 **pptr, const struct gossip_getchannels_entry *entry); u8 **pptr, const struct gossip_getchannels_entry *entry);

1
tools/generate-wire.py

@ -37,6 +37,7 @@ type2size = {
varlen_structs = [ varlen_structs = [
'peer_features', 'peer_features',
'gossip_getnodes_entry', 'gossip_getnodes_entry',
'gossip_getchannels_entry',
'failed_htlc', 'failed_htlc',
'utxo', 'utxo',
'bitcoin_tx', 'bitcoin_tx',

5
wire/wire_io.h

@ -5,9 +5,8 @@
#include <ccan/io/io.h> #include <ccan/io/io.h>
#include <ccan/short_types/short_types.h> #include <ccan/short_types/short_types.h>
/* We don't allow > 64M msgs: enough for 483 64k failure msgs. */ /* We don't allow > 128M msgs: enough for more than 1M channels in gossip_getchannels_entry. */
/* FIXME: Too big, but allows the million-channels project at 327077670 bytes */ #define WIRE_LEN_LIMIT (1 << 27)
#define WIRE_LEN_LIMIT (1 << 29)
typedef be32 wire_len_t; typedef be32 wire_len_t;
#define wirelen_to_cpu be32_to_cpu #define wirelen_to_cpu be32_to_cpu

Loading…
Cancel
Save