diff --git a/common/gossip_store.h b/common/gossip_store.h index f1a8525df79a..eb7e9c25473b 100644 --- a/common/gossip_store.h +++ b/common/gossip_store.h @@ -34,16 +34,6 @@ struct gossip_rcvd_filter; */ #define GOSSIP_STORE_PUSH_BIT 0x4000U -/** - * Bit of flags used to define a rate-limited record (do not rebroadcast) - */ -#define GOSSIP_STORE_RATELIMIT_BIT 0x2000U - -/** - * Bit of flags used to mark a channel announcement as inactive (needs channel updates.) - */ -#define GOSSIP_STORE_ZOMBIE_BIT 0x1000U - /** * Bit of flags used to mark a channel announcement closed (not deleted for 12 blocks) */ diff --git a/common/gossmap.c b/common/gossmap.c index a61947bc5636..0a93e6614553 100644 --- a/common/gossmap.c +++ b/common/gossmap.c @@ -51,6 +51,7 @@ HTABLE_DEFINE_TYPE(ptrint_t, nodeidx_id, nodeid_hash, nodeidx_eq_id, struct gossmap { /* The file descriptor and filename to monitor */ int fd; + /* NULL means we don't own fd */ const char *fname; /* The memory map of the file: u8 for arithmetic portability */ @@ -79,6 +80,20 @@ struct gossmap { /* local messages, if any. */ const u8 *local; + + /* Callbacks for different events: return false to fail. */ + void (*cupdate_fail)(struct gossmap *map, + const struct short_channel_id_dir *scidd, + u16 cltv_expiry_delta, + u32 fee_base_msat, + u32 fee_proportional_millionths, + void *cb_arg); + bool (*unknown_record)(struct gossmap *map, + int type, + size_t off, + size_t msglen, + void *cb_arg); + void *cb_arg; }; /* Accessors for the gossmap */ @@ -156,6 +171,18 @@ static int map_feature_test(const struct gossmap *map, return -1; } +/* Helper callback which simply increments counter */ +static void cupdate_fail_inc_ctr(struct gossmap *map, + const struct short_channel_id_dir *scidd, + u16 cltv_expiry_delta, + u32 fee_base_msat, + u32 fee_proportional_millionths, + void *cb_arg) +{ + size_t *num = cb_arg; + (*num)++; +} + /* These values can change across calls to gossmap_check. */ u32 gossmap_max_node_idx(const struct gossmap *map) { @@ -318,14 +345,9 @@ static u32 init_chan_arr(struct gossmap_chan *chan_arr, size_t start) for (i = start; i < tal_count(chan_arr) - 1; i++) { chan_arr[i].cann_off = i + 1; chan_arr[i].plus_scid_off = 0; - /* We don't need to initialize this, *but* on some platforms - * (ppc, arm64) valgrind complains: this is a bitfield shared - * with plus_scid_off */ - chan_arr[i].private = false; } chan_arr[i].cann_off = UINT_MAX; chan_arr[i].plus_scid_off = 0; - chan_arr[i].private = false; return start; } @@ -350,13 +372,11 @@ static struct gossmap_chan *next_free_chan(struct gossmap *map) static struct gossmap_chan *new_channel(struct gossmap *map, u32 cannounce_off, u32 plus_scid_off, - bool private, u32 n1idx, u32 n2idx) { struct gossmap_chan *chan = next_free_chan(map); chan->cann_off = cannounce_off; - chan->private = private; chan->plus_scid_off = plus_scid_off; chan->cupdate_off[0] = chan->cupdate_off[1] = 0; memset(chan->half, 0, sizeof(chan->half)); @@ -421,8 +441,7 @@ void gossmap_remove_node(struct gossmap *map, struct gossmap_node *node) * * [`point`:`node_id_2`] */ static struct gossmap_chan *add_channel(struct gossmap *map, - size_t cannounce_off, - bool private) + size_t cannounce_off) { /* Note that first two bytes are message type */ const size_t feature_len_off = 2 + (64 + 64 + 64 + 64); @@ -440,12 +459,9 @@ static struct gossmap_chan *add_channel(struct gossmap *map, map_nodeid(map, cannounce_off + plus_scid_off + 8, &node_id[0]); map_nodeid(map, cannounce_off + plus_scid_off + 8 + PUBKEY_CMPR_LEN, &node_id[1]); - /* We can have a channel upgrade from private->public, but - * that's the only time we get duplicates */ + /* We 1should not get duplicates. */ scid.u64 = map_be64(map, cannounce_off + plus_scid_off); - chan = gossmap_find_chan(map, &scid); - if (chan) - gossmap_remove_chan(map, chan); + assert(!gossmap_find_chan(map, &scid)); /* We carefully map pointers to indexes, since new_node can move them! */ n[0] = gossmap_find_node(map, &node_id[0]); @@ -460,7 +476,7 @@ static struct gossmap_chan *add_channel(struct gossmap *map, else nidx[1] = new_node(map); - chan = new_channel(map, cannounce_off, plus_scid_off, private, + chan = new_channel(map, cannounce_off, plus_scid_off, nidx[0], nidx[1]); /* Now we have a channel, we can add nodes to htable */ @@ -489,7 +505,7 @@ static struct gossmap_chan *add_channel(struct gossmap *map, * * [`u32`:`fee_proportional_millionths`] * * [`u64`:`htlc_maximum_msat`] */ -static bool update_channel(struct gossmap *map, size_t cupdate_off) +static void update_channel(struct gossmap *map, size_t cupdate_off) { /* Note that first two bytes are message type */ const size_t scid_off = cupdate_off + 2 + (64 + 32); @@ -500,44 +516,49 @@ static bool update_channel(struct gossmap *map, size_t cupdate_off) const size_t fee_base_off = htlc_minimum_off + 8; const size_t fee_prop_off = fee_base_off + 4; const size_t htlc_maximum_off = fee_prop_off + 4; - struct short_channel_id scid; + struct short_channel_id_dir scidd; struct gossmap_chan *chan; struct half_chan hc; u8 chanflags; - bool dumb_values; + u32 base_fee, proportional_fee; + u16 delay; - scid.u64 = map_be64(map, scid_off); - chan = gossmap_find_chan(map, &scid); + scidd.scid.u64 = map_be64(map, scid_off); + chan = gossmap_find_chan(map, &scidd.scid); /* This can happen if channel gets deleted! */ if (!chan) - return false; + return; /* We round this *down*, since too-low min is more conservative */ hc.htlc_min = u64_to_fp16(map_be64(map, htlc_minimum_off), false); hc.htlc_max = u64_to_fp16(map_be64(map, htlc_maximum_off), true); chanflags = map_u8(map, channel_flags_off); - hc.enabled = !(chanflags & 2); - hc.base_fee = map_be32(map, fee_base_off); - hc.proportional_fee = map_be32(map, fee_prop_off); - hc.delay = map_be16(map, cltv_expiry_delta_off); - + hc.enabled = !(chanflags & ROUTING_FLAGS_DISABLED); + scidd.dir = (chanflags & ROUTING_FLAGS_DIRECTION); + base_fee = map_be32(map, fee_base_off); + proportional_fee = map_be32(map, fee_prop_off); + delay = map_be16(map, cltv_expiry_delta_off); + + hc.base_fee = base_fee; + hc.proportional_fee = proportional_fee; + hc.delay = delay; /* Check they fit: we turn off if not. */ - if (hc.base_fee != map_be32(map, fee_base_off) - || hc.proportional_fee != map_be32(map, fee_prop_off) - || hc.delay != map_be16(map, cltv_expiry_delta_off)) { - dumb_values = true; + if (hc.base_fee != base_fee + || hc.proportional_fee != proportional_fee + || hc.delay != delay) { + if (map->cupdate_fail) + map->cupdate_fail(map, &scidd, + delay, base_fee, proportional_fee, + map->cb_arg); hc.htlc_max = 0; hc.enabled = false; - } else - dumb_values = false; + } /* Preserve this */ hc.nodeidx = chan->half[chanflags & 1].nodeidx; chan->half[chanflags & 1] = hc; chan->cupdate_off[chanflags & 1] = cupdate_off; - - return !dumb_values; } static void remove_channel_by_deletemsg(struct gossmap *map, size_t del_off) @@ -591,10 +612,14 @@ static void node_announcement(struct gossmap *map, size_t nann_off) n->nann_off = nann_off; } -static void reopen_store(struct gossmap *map, size_t ended_off) +static bool reopen_store(struct gossmap *map, size_t ended_off) { - int fd = open(map->fname, O_RDONLY); + int fd; + if (!map->fname) + errx(1, "Need to reopen, but not fname!"); + + fd = open(map->fname, O_RDONLY); if (fd < 0) err(1, "Failed to reopen %s", map->fname); @@ -603,15 +628,16 @@ static void reopen_store(struct gossmap *map, size_t ended_off) close(map->fd); map->fd = fd; - gossmap_refresh(map, NULL); + return gossmap_refresh_mayfail(map, NULL); } -static bool map_catchup(struct gossmap *map, size_t *num_rejected) +/* Returns false only if unknown_cb returns false */ +static bool map_catchup(struct gossmap *map, bool *changed) { size_t reclen; - bool changed = false; - size_t num_bad = 0; + if (changed) + *changed = false; for (; map->map_end + sizeof(struct gossip_hdr) < map->map_size; map->map_end += reclen) { struct gossip_hdr ghdr; @@ -625,9 +651,6 @@ static bool map_catchup(struct gossmap *map, size_t *num_rejected) if (flags & GOSSIP_STORE_DELETED_BIT) continue; - if (flags & GOSSIP_STORE_ZOMBIE_BIT) - continue; - /* Partial write, this can happen. */ if (map->map_end + reclen > map->map_size) break; @@ -635,32 +658,36 @@ static bool map_catchup(struct gossmap *map, size_t *num_rejected) off = map->map_end + sizeof(ghdr); type = map_be16(map, off); if (type == WIRE_CHANNEL_ANNOUNCEMENT) - add_channel(map, off, false); + add_channel(map, off); else if (type == WIRE_CHANNEL_UPDATE) - num_bad += !update_channel(map, off); + update_channel(map, off); else if (type == WIRE_GOSSIP_STORE_DELETE_CHAN) remove_channel_by_deletemsg(map, off); else if (type == WIRE_NODE_ANNOUNCEMENT) node_announcement(map, off); - else if (type == WIRE_GOSSIP_STORE_ENDED) - reopen_store(map, off); - else + else if (type == WIRE_GOSSIP_STORE_ENDED && map->fname) { + /* This can recurse! */ + if (!reopen_store(map, off)) + return false; + } else { + if (map->unknown_record + && !map->unknown_record(map, type, off, + reclen - sizeof(ghdr), + map->cb_arg)) { + return false; + } continue; + } - changed = true; + if (changed) + *changed = true; } - if (num_rejected) - *num_rejected = num_bad; - return changed; + return true; } -static bool load_gossip_store(struct gossmap *map, size_t *num_rejected) +static bool load_gossip_store(struct gossmap *map) { - map->fd = open(map->fname, O_RDONLY); - if (map->fd < 0) - return false; - map->map_size = lseek(map->fd, 0, SEEK_END); map->local = NULL; /* If this fails, we fall back to read */ @@ -670,9 +697,6 @@ static bool load_gossip_store(struct gossmap *map, size_t *num_rejected) /* We only support major version 0 */ if (GOSSIP_STORE_MAJOR_VERSION(map_u8(map, 0)) != 0) { - close(map->fd); - if (map->mmap) - munmap(map->mmap, map->map_size); errno = EINVAL; return false; } @@ -695,7 +719,7 @@ static bool load_gossip_store(struct gossmap *map, size_t *num_rejected) map->freed_nodes = init_node_arr(map->node_arr, 0); map->map_end = 1; - map_catchup(map, num_rejected); + map_catchup(map, NULL); return true; } @@ -706,6 +730,9 @@ static void destroy_map(struct gossmap *map) for (size_t i = 0; i < tal_count(map->node_arr); i++) free(map->node_arr[i].chan_idxs); + + if (map->fname) + close(map->fd); } /* Local modifications. We only expect a few, so we use a simple @@ -902,8 +929,7 @@ void gossmap_apply_localmods(struct gossmap *map, continue; /* Create new channel, pointing into local. */ - chan = add_channel(map, map->map_size + mod->local_off, - true); + chan = add_channel(map, map->map_size + mod->local_off); } /* Save old, overwrite (keep nodeidx) */ @@ -950,7 +976,7 @@ void gossmap_remove_localmods(struct gossmap *map, map->local = NULL; } -bool gossmap_refresh(struct gossmap *map, size_t *num_rejected) +bool gossmap_refresh_mayfail(struct gossmap *map, bool *updated) { off_t len; @@ -959,8 +985,11 @@ bool gossmap_refresh(struct gossmap *map, size_t *num_rejected) /* If file has gotten larger, try rereading */ len = lseek(map->fd, 0, SEEK_END); - if (len == map->map_size) - return false; + if (len == map->map_size) { + if (updated) + *updated = false; + return true; + } if (map->mmap) munmap(map->mmap, map->map_size); @@ -968,7 +997,33 @@ bool gossmap_refresh(struct gossmap *map, size_t *num_rejected) map->mmap = mmap(NULL, map->map_size, PROT_READ, MAP_SHARED, map->fd, 0); if (map->mmap == MAP_FAILED) map->mmap = NULL; - return map_catchup(map, num_rejected); + + return map_catchup(map, updated); +} + +bool gossmap_refresh(struct gossmap *map, size_t *num_rejected) +{ + bool updated; + void (*old_cupdate_fail)(struct gossmap *map, + const struct short_channel_id_dir *scidd, + u16 cltv_expiry_delta, + u32 fee_base_msat, + u32 fee_proportional_millionths, + void *cb_arg); + + /* If they asked for counter, temporarily override cb */ + old_cupdate_fail = map->cupdate_fail; + if (num_rejected) { + map->cupdate_fail = cupdate_fail_inc_ctr; + map->cb_arg = num_rejected; + } + + /* This can only fail if you set unknown_cb, and it failed. So wrong API! */ + if (!gossmap_refresh_mayfail(map, &updated)) + abort(); + + map->cupdate_fail = old_cupdate_fail; + return updated; } struct gossmap *gossmap_load(const tal_t *ctx, const char *filename, @@ -976,10 +1031,47 @@ struct gossmap *gossmap_load(const tal_t *ctx, const char *filename, { map = tal(ctx, struct gossmap); map->fname = tal_strdup(map, filename); - if (load_gossip_store(map, num_channel_updates_rejected)) - tal_add_destructor(map, destroy_map); - else - map = tal_free(map); + map->fd = open(map->fname, O_RDONLY); + if (map->fd < 0) + return tal_free(map); + tal_add_destructor(map, destroy_map); + if (num_channel_updates_rejected) { + *num_channel_updates_rejected = 0; + map->cupdate_fail = cupdate_fail_inc_ctr; + map->cb_arg = num_channel_updates_rejected; + } + map->unknown_record = NULL; + + if (!load_gossip_store(map)) + return tal_free(map); + map->cupdate_fail = NULL; + return map; +} + +struct gossmap *gossmap_load_fd_(const tal_t *ctx, int fd, + void (*cupdate_fail)(struct gossmap *map, + const struct short_channel_id_dir *scidd, + u16 cltv_expiry_delta, + u32 fee_base_msat, + u32 fee_proportional_millionths, + void *cb_arg), + bool (*unknown_record)(struct gossmap *map, + int type, + size_t off, + size_t msglen, + void *cb_arg), + void *cb_arg) +{ + map = tal(ctx, struct gossmap); + map->fname = NULL; + map->fd = fd; + map->cupdate_fail = cupdate_fail; + map->unknown_record = unknown_record; + map->cb_arg = cb_arg; + tal_add_destructor(map, destroy_map); + + if (!load_gossip_store(map)) + return tal_free(map); return map; } @@ -995,6 +1087,12 @@ void gossmap_node_get_id(const struct gossmap *map, + 8 + PUBKEY_CMPR_LEN*dir, id); } +bool gossmap_chan_is_localmod(const struct gossmap *map, + const struct gossmap_chan *c) +{ + return c->cann_off >= map->map_size; +} + bool gossmap_chan_get_capacity(const struct gossmap *map, const struct gossmap_chan *c, struct amount_sat *amount) @@ -1004,16 +1102,9 @@ bool gossmap_chan_get_capacity(const struct gossmap *map, u16 type; /* Fail for local channels */ - if (c->cann_off >= map->map_size) + if (gossmap_chan_is_localmod(map, c)) return false; - /* For private, we need to go back WIRE_GOSSIP_STORE_PRIVATE_CHANNEL, - * which is 8 (satoshis) + 2 (len) */ - if (c->private) { - *amount = amount_sat(map_be64(map, c->cann_off - 8 - 2)); - return true; - } - /* Skip over this record to next; expect a gossip_store_channel_amount */ off = c->cann_off - sizeof(ghdr); map_copy(map, off, &ghdr, sizeof(ghdr)); @@ -1113,9 +1204,9 @@ struct gossmap_chan *gossmap_next_chan(const struct gossmap *map, return chan_iter(map, prev - map->chan_arr + 1); } -bool gossmap_chan_capacity(const struct gossmap_chan *chan, - int direction, - struct amount_msat amount) +bool gossmap_chan_has_capacity(const struct gossmap_chan *chan, + int direction, + struct amount_msat amount) { if (amount_msat_less_fp16(amount, chan->half[direction].htlc_min)) return false; @@ -1133,14 +1224,10 @@ u8 *gossmap_chan_get_announce(const tal_t *ctx, { u16 len; u8 *msg; - u32 pre_off; - /* We need to go back to struct gossip_hdr to get len */ - if (c->private) - pre_off = 2 + 8 + 2 + sizeof(struct gossip_hdr); - else - pre_off = sizeof(struct gossip_hdr); - len = map_be16(map, c->cann_off - pre_off + if (gossmap_chan_is_localmod(map, c)) + return NULL; + len = map_be16(map, c->cann_off - sizeof(struct gossip_hdr) + offsetof(struct gossip_hdr, len)); msg = tal_arr(ctx, u8, len); @@ -1211,6 +1298,26 @@ u8 *gossmap_chan_get_features(const tal_t *ctx, return ret; } +/* Return the channel_update (or NULL if !gossmap_chan_set) */ +u8 *gossmap_chan_get_update(const tal_t *ctx, + const struct gossmap *map, + const struct gossmap_chan *chan, + int dir) +{ + u16 len; + u8 *msg; + + if (chan->cupdate_off[dir] == 0) + return NULL; + + len = map_be16(map, chan->cupdate_off[dir] - sizeof(struct gossip_hdr) + + offsetof(struct gossip_hdr, len)); + msg = tal_arr(ctx, u8, len); + + map_copy(map, chan->cupdate_off[dir], msg, len); + return msg; +} + /* BOLT #7: * 1. type: 258 (`channel_update`) * 2. data: diff --git a/common/gossmap.h b/common/gossmap.h index edaec7415f6d..d9d2ad3994a7 100644 --- a/common/gossmap.h +++ b/common/gossmap.h @@ -18,9 +18,8 @@ struct gossmap_node { struct gossmap_chan { u32 cann_off; - u32 private: 1; /* Technically redundant, but we have a hole anyway: from cann_off */ - u32 plus_scid_off: 31; + u32 plus_scid_off; /* Offsets of cupdates (0 if missing). Logically inside half_chan, * but that would add padding. */ u32 cupdate_off[2]; @@ -45,10 +44,44 @@ struct gossmap_chan { struct gossmap *gossmap_load(const tal_t *ctx, const char *filename, size_t *num_channel_updates_rejected); +/* Version which uses existing fd */ +#define gossmap_load_fd(ctx, fd, cupdate_fail, unknown_record, cbarg) \ + gossmap_load_fd_((ctx), (fd), \ + typesafe_cb_preargs(void, void *, (cupdate_fail), (cbarg), \ + struct gossmap *, \ + const struct short_channel_id_dir *, \ + u16 cltv_expiry_delta, \ + u32 fee_base_msat, \ + u32 fee_proportional_millionths), \ + typesafe_cb_preargs(bool, void *, (unknown_record), (cbarg), \ + struct gossmap *, \ + int type, \ + size_t off, \ + size_t msglen), \ + (cbarg)) + +struct gossmap *gossmap_load_fd_(const tal_t *ctx, int fd, + void (*cupdate_fail)(struct gossmap *map, + const struct short_channel_id_dir *scidd, + u16 cltv_expiry_delta, + u32 fee_base_msat, + u32 fee_proportional_millionths, + void *cb_arg), + bool (*unknown_record)(struct gossmap *map, + int type, + size_t off, + size_t msglen, + void *cb_arg), + void *cb_arg); + + /* Call this before using to ensure it's up-to-date. Returns true if something * was updated. Note: this can scramble node and chan indexes! */ bool gossmap_refresh(struct gossmap *map, size_t *num_channel_updates_rejected); +/* Call this if you have set unknown_cb, and thus this can fail! */ +bool gossmap_refresh_mayfail(struct gossmap *map, bool *updated); + /* Local modifications. */ struct gossmap_localmods *gossmap_localmods_new(const tal_t *ctx); @@ -85,6 +118,10 @@ void gossmap_apply_localmods(struct gossmap *map, void gossmap_remove_localmods(struct gossmap *map, const struct gossmap_localmods *localmods); +/* Is this channel a localmod? */ +bool gossmap_chan_is_localmod(const struct gossmap *map, + const struct gossmap_chan *c); + /* Each channel has a unique (low) index. */ u32 gossmap_node_idx(const struct gossmap *map, const struct gossmap_node *node); u32 gossmap_chan_idx(const struct gossmap *map, const struct gossmap_chan *chan); @@ -124,11 +161,17 @@ bool gossmap_chan_get_capacity(const struct gossmap *map, const struct gossmap_chan *c, struct amount_sat *amount); -/* Get the announcement msg which created this chan */ +/* Get the announcement msg which created this chan (NULL for localmods) */ u8 *gossmap_chan_get_announce(const tal_t *ctx, const struct gossmap *map, const struct gossmap_chan *c); +/* Do we have a node_announcement for this onde ? */ +static inline bool gossmap_node_announced(const struct gossmap_node *node) +{ + return node->nann_off != 0; +} + /* Get the announcement msg (if any) for this node. */ u8 *gossmap_node_get_announce(const tal_t *ctx, const struct gossmap *map, @@ -154,6 +197,12 @@ u8 *gossmap_node_get_features(const tal_t *ctx, const struct gossmap *map, const struct gossmap_node *n); +/* Return the channel_update (or NULL if !gossmap_chan_set) */ +u8 *gossmap_chan_get_update(const tal_t *ctx, + const struct gossmap *map, + const struct gossmap_chan *chan, + int dir); + /* Returns details from channel_update (must be gossmap_chan_set, and * does not work for local_updatechan)! */ void gossmap_chan_get_update_details(const struct gossmap *map, @@ -180,9 +229,9 @@ struct gossmap_node *gossmap_nth_node(const struct gossmap *map, int n); /* Can this channel send this amount? */ -bool gossmap_chan_capacity(const struct gossmap_chan *chan, - int direction, - struct amount_msat amount); +bool gossmap_chan_has_capacity(const struct gossmap_chan *chan, + int direction, + struct amount_msat amount); /* Remove a channel from the map (warning! realloc can move gossmap_chan * and gossmap_node ptrs!) */ diff --git a/common/route.c b/common/route.c index 64beb206313d..1fc5ffd3b7ef 100644 --- a/common/route.c +++ b/common/route.c @@ -15,7 +15,7 @@ bool route_can_carry_even_disabled(const struct gossmap *map, return false; /* Amount 0 is a special "ignore min" probe case */ if (!amount_msat_eq(amount, AMOUNT_MSAT(0)) - && !gossmap_chan_capacity(c, dir, amount)) + && !gossmap_chan_has_capacity(c, dir, amount)) return false; return true; } diff --git a/common/test/run-gossmap_canned.c b/common/test/run-gossmap_canned.c index 2bd9f6cc2e28..4b3f77190a7c 100644 --- a/common/test/run-gossmap_canned.c +++ b/common/test/run-gossmap_canned.c @@ -332,7 +332,7 @@ int main(int argc, char *argv[]) assert(short_channel_id_from_str("110x1x0", 7, &scid12)); assert(gossmap_find_chan(map, &scid12)); - assert(!gossmap_find_chan(map, &scid12)->private); + assert(!gossmap_chan_is_localmod(map, gossmap_find_chan(map, &scid12))); assert(gossmap_chan_get_capacity(map, gossmap_find_chan(map, &scid12), &capacity)); assert(amount_sat_eq(capacity, AMOUNT_SAT(1000000))); diff --git a/common/test/run-gossmap_local.c b/common/test/run-gossmap_local.c index b95e6af5f14c..f1519b0eb276 100644 --- a/common/test/run-gossmap_local.c +++ b/common/test/run-gossmap_local.c @@ -358,9 +358,9 @@ int main(int argc, char *argv[]) assert(short_channel_id_from_str("103x1x0", 7, &scid23)); assert(short_channel_id_from_str("110x1x0", 7, &scid12)); assert(gossmap_find_chan(map, &scid23)); - assert(!gossmap_find_chan(map, &scid23)->private); + assert(!gossmap_chan_is_localmod(map, gossmap_find_chan(map, &scid23))); assert(gossmap_find_chan(map, &scid12)); - assert(!gossmap_find_chan(map, &scid12)->private); + assert(!gossmap_chan_is_localmod(map, gossmap_find_chan(map, &scid12))); assert(gossmap_chan_get_capacity(map, gossmap_find_chan(map, &scid23), &capacity)); assert(amount_sat_eq(capacity, AMOUNT_SAT(1000000))); diff --git a/connectd/gossip_store.c b/connectd/gossip_store.c index ca1fdd4deec5..c3e94dbf7bd1 100644 --- a/connectd/gossip_store.c +++ b/connectd/gossip_store.c @@ -112,7 +112,6 @@ static bool public_msg_type(enum peer_wire type) u8 *gossip_store_next(const tal_t *ctx, int *gossip_store_fd, u32 timestamp_min, u32 timestamp_max, - bool with_spam, size_t *off, size_t *end) { u8 *msg = NULL; @@ -122,7 +121,6 @@ u8 *gossip_store_next(const tal_t *ctx, struct gossip_hdr hdr; u16 msglen, flags; u32 checksum, timestamp; - bool ratelimited; int type, r; r = pread(*gossip_store_fd, &hdr, sizeof(hdr), *off); @@ -131,7 +129,6 @@ u8 *gossip_store_next(const tal_t *ctx, msglen = be16_to_cpu(hdr.len); flags = be16_to_cpu(hdr.flags); - ratelimited = (flags & GOSSIP_STORE_RATELIMIT_BIT); /* Skip any deleted/dying entries. */ if (flags & (GOSSIP_STORE_DELETED_BIT|GOSSIP_STORE_DYING_BIT)) { @@ -172,8 +169,6 @@ u8 *gossip_store_next(const tal_t *ctx, /* Ignore gossipd internal messages. */ } else if (!public_msg_type(type)) { msg = tal_free(msg); - } else if (!with_spam && ratelimited) { - msg = tal_free(msg); } } diff --git a/connectd/gossip_store.h b/connectd/gossip_store.h index 12cd1d6c19eb..4067d414148f 100644 --- a/connectd/gossip_store.h +++ b/connectd/gossip_store.h @@ -13,7 +13,6 @@ u8 *gossip_store_next(const tal_t *ctx, int *gossip_store_fd, u32 timestamp_min, u32 timestamp_max, - bool with_spam, size_t *off, size_t *end); /** diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 3c50a2117ddd..72b160353059 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -509,7 +509,6 @@ static u8 *maybe_from_gossip_store(const tal_t *ctx, struct peer *peer) msg = gossip_store_next(ctx, &peer->daemon->gossip_store_fd, peer->gs.timestamp_min, peer->gs.timestamp_max, - false, &peer->gs.off, &peer->daemon->gossip_store_end); /* Don't send back gossip they sent to us! */ diff --git a/contrib/pyln-client/pyln/client/gossmap.py b/contrib/pyln-client/pyln/client/gossmap.py index 88641cbba617..b7a27bcfadfb 100755 --- a/contrib/pyln-client/pyln/client/gossmap.py +++ b/contrib/pyln-client/pyln/client/gossmap.py @@ -16,8 +16,6 @@ GOSSIP_STORE_MAJOR_VERSION_MASK = 0xE0 GOSSIP_STORE_LEN_DELETED_BIT = 0x8000 GOSSIP_STORE_LEN_PUSH_BIT = 0x4000 -GOSSIP_STORE_LEN_RATELIMIT_BIT = 0x2000 -GOSSIP_STORE_ZOMBIE_BIT = 0x1000 # These duplicate constants in lightning/gossipd/gossip_store_wiregen.h WIRE_GOSSIP_STORE_PRIVATE_CHANNEL = 4104 @@ -91,8 +89,6 @@ def __init__(self, buf: bytes, off: int): self.flags, self.length, self.crc, self.timestamp = struct.unpack('>HHII', buf) self.off = off self.deleted = (self.flags & GOSSIP_STORE_LEN_DELETED_BIT) != 0 - self.ratelimit = (self.flags & GOSSIP_STORE_LEN_RATELIMIT_BIT) != 0 - self.zombie = (self.flags & GOSSIP_STORE_ZOMBIE_BIT) != 0 class GossmapHalfchannel(object): @@ -624,8 +620,6 @@ def refresh(self): break if hdr.deleted: # Skip deleted records continue - if hdr.zombie: - continue rectype, = struct.unpack(">H", rec[:2]) if rectype == channel_announcement.number: diff --git a/contrib/pyln-client/pyln/client/gossmapstats.py b/contrib/pyln-client/pyln/client/gossmapstats.py index 5479e2dc908b..7c6458696d20 100644 --- a/contrib/pyln-client/pyln/client/gossmapstats.py +++ b/contrib/pyln-client/pyln/client/gossmapstats.py @@ -31,10 +31,6 @@ def filter_halfchannels(self, predicate: Callable[[GossmapHalfchannel], bool], c return hc0 + hc1 # Now a bunch of predefined specific filter methods - def filter_nodes_ratelimited(self, nodes: Optional[Iterable[GossmapNode]] = None) -> List[GossmapNode]: - """ Filters nodes being marked by cln as ratelimited, when they send out too many updates. """ - return self.filter_nodes(lambda n: n.hdr is not None and n.hdr.ratelimit, nodes) - def filter_nodes_unannounced(self, nodes: Optional[Iterable[GossmapNode]] = None) -> List[GossmapNode]: """ Filters nodes that are only known by a channel, i.e. missing a node_announcement. Usually happens when a peer has been offline for a while. """ @@ -124,10 +120,6 @@ def filter_halfchannels_disabled(self, channels: Optional[Iterable[GossmapChanne """ Filters half-channels that are disabled. """ return self.filter_halfchannels(lambda hc: hc.disabled, channels) - def filter_halfchannels_ratelimited(self, channels: Optional[Iterable[GossmapChannel]] = None) -> List[GossmapHalfchannel]: - """ Filters half-channels that are being marked as ratelimited for sending out too many updates. """ - return self.filter_halfchannels(lambda hc: hc.hdr.ratelimit, channels) - def quantiles_nodes_channel_count(self, tiles=100, nodes: Optional[Iterable[GossmapNode]] = None) -> List[float]: if nodes is None: nodes = self.g.nodes.values() @@ -161,8 +153,6 @@ def print_stats(self): print("CONSISTENCY") print(f" - {len(self.filter_nodes_unannounced())} orphan nodes without a node_announcement, only known from a channel_announcement.") print(f" - {len(self.g.orphan_channel_updates)} orphan channel_updates without a prior channel_announcement.") - print(f" - {len(self.filter_nodes_ratelimited())} nodes marked as ratelimited. (sending too many updates).") - print(f" - {len(self.filter_halfchannels_ratelimited())} half-channels marked as ratelimited. (sending too many updates).") print(f" - {len(self.filter_channels_nosatoshis())} channels without capacity (missing WIRE_GOSSIP_STORE_CHANNEL_AMOUNT). Should be 0.") print("") diff --git a/devtools/dump-gossipstore.c b/devtools/dump-gossipstore.c index 09c6f3a1e428..a7fae9145d8e 100644 --- a/devtools/dump-gossipstore.c +++ b/devtools/dump-gossipstore.c @@ -68,13 +68,11 @@ int main(int argc, char *argv[]) u16 flags = be16_to_cpu(hdr.flags); u16 msglen = be16_to_cpu(hdr.len); u8 *msg, *inner; - bool deleted, push, ratelimit, zombie, dying; + bool deleted, push, dying; u32 blockheight; deleted = (flags & GOSSIP_STORE_DELETED_BIT); push = (flags & GOSSIP_STORE_PUSH_BIT); - ratelimit = (flags & GOSSIP_STORE_RATELIMIT_BIT); - zombie = (flags & GOSSIP_STORE_ZOMBIE_BIT); dying = (flags & GOSSIP_STORE_DYING_BIT); msg = tal_arr(NULL, u8, msglen); @@ -85,11 +83,9 @@ int main(int argc, char *argv[]) != crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)) warnx("Checksum verification failed"); - printf("%zu: %s%s%s%s%s", off, + printf("%zu: %s%s%s", off, deleted ? "DELETED " : "", push ? "PUSH " : "", - ratelimit ? "RATE-LIMITED " : "", - zombie ? "ZOMBIE " : "", dying ? "DYING " : ""); if (print_timestamp) printf("T=%u ", be32_to_cpu(hdr.timestamp)); diff --git a/doc/contribute-to-core-lightning/contribute-to-core-lightning.md b/doc/contribute-to-core-lightning/contribute-to-core-lightning.md index c5506b4b374c..5e83adc3428a 100644 --- a/doc/contribute-to-core-lightning/contribute-to-core-lightning.md +++ b/doc/contribute-to-core-lightning/contribute-to-core-lightning.md @@ -160,7 +160,7 @@ The flags currently defined are: ``` #define DELETED 0x8000 #define PUSH 0x4000 -#define RATELIMIT 0x2000 +#define DYING 0x0800 ``` @@ -169,7 +169,7 @@ Deleted fields should be ignored: on restart, they will be removed as the gossip The push flag indicates gossip which is generated locally: this is important for gossip timestamp filtering, where peers request gossip and we always send our own gossip messages even if the timestamp wasn't within their request. -The ratelimit flag indicates that this gossip message came too fast: we record it, but don't relay it to peers. +The dying flag indicates that this channel has been spent, but we keep it around for 12 blocks in case it's actually a splice. Other flags should be ignored. @@ -234,4 +234,4 @@ This is placed in the gossip_store file when a funding transaction is spent. `b If you are keeping the file open to watch for changes: - The file is append-only, so you can simply try reading more records using inotify (or equivalent) or simply checking every few seconds. -- If you see a `gossip_store_ended` message, reopen the file. \ No newline at end of file +- If you see a `gossip_store_ended` message, reopen the file. diff --git a/gossipd/Makefile b/gossipd/Makefile index 42e79f256963..c18a5d32574a 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -5,10 +5,12 @@ GOSSIPD_HEADERS_WSRC := gossipd/gossipd_wiregen.h \ gossipd/gossip_store_wiregen.h \ gossipd/gossipd.h \ gossipd/gossip_store.h \ + gossipd/gossmap_manage.h \ gossipd/queries.h \ - gossipd/routing.h \ + gossipd/txout_failures.h \ + gossipd/sigcheck.h \ gossipd/seeker.h -GOSSIPD_HEADERS := $(GOSSIPD_HEADERS_WSRC) gossipd/broadcast.h +GOSSIPD_HEADERS := $(GOSSIPD_HEADERS_WSRC) GOSSIPD_SRC := $(GOSSIPD_HEADERS_WSRC:.h=.c) GOSSIPD_OBJS := $(GOSSIPD_SRC:.c=.o) @@ -42,6 +44,8 @@ GOSSIPD_COMMON_OBJS := \ common/dev_disconnect.o \ common/ecdh_hsmd.o \ common/features.o \ + common/gossmap.o \ + common/fp16.o \ common/status_wiregen.o \ common/key_derive.o \ common/lease_rates.o \ diff --git a/gossipd/broadcast.h b/gossipd/broadcast.h deleted file mode 100644 index d07f98b38ff2..000000000000 --- a/gossipd/broadcast.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef LIGHTNING_GOSSIPD_BROADCAST_H -#define LIGHTNING_GOSSIPD_BROADCAST_H -#include "config.h" - -#include - -/* This is nested inside a node, chan or half_chan; rewriting the store can - * cause it to change! */ -struct broadcastable { - /* This is also the offset within the gossip_store; even with 1M - * channels we still have a factor of 8 before this wraps. */ - u32 index; - u32 timestamp; -}; - -static inline void broadcastable_init(struct broadcastable *bcast) -{ - bcast->index = 0; -} -#endif /* LIGHTNING_GOSSIPD_BROADCAST_H */ diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index 2c03719d54d7..d6b218e17c7e 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -10,18 +10,24 @@ #include #include #include +#include +#include +#include #include #include #include #include +/* Obsolete ZOMBIE bit */ +#define GOSSIP_STORE_ZOMBIE_BIT_V13 0x1000U + #define GOSSIP_STORE_TEMP_FILENAME "gossip_store.tmp" -/* We write it as major version 0, minor version 13 */ -#define GOSSIP_STORE_VER ((0 << 5) | 13) +/* We write it as major version 0, minor version 14 */ +#define GOSSIP_STORE_VER ((0 << 5) | 14) struct gossip_store { - /* This is false when we're loading */ - bool writable; + /* Back pointer. */ + struct daemon *daemon; int fd; u8 version; @@ -29,19 +35,6 @@ struct gossip_store { /* Offset of current EOF */ u64 len; - /* Counters for entries in the gossip_store entries. This is used to - * decide whether we should rewrite the on-disk store or not. - * Note: count includes deleted. */ - size_t count, deleted; - - /* Handle to the routing_state to retrieve additional information, - * should it be needed */ - struct routing_state *rstate; - - /* Disable compaction if we encounter an error during a prior - * compaction */ - bool disable_compaction; - /* Timestamp of store when we opened it (0 if we created it) */ u32 timestamp; }; @@ -68,8 +61,7 @@ static ssize_t gossip_pwritev(int fd, const struct iovec *iov, int iovcnt, } #endif /* !HAVE_PWRITEV */ -static bool append_msg(int fd, const u8 *msg, u32 timestamp, - bool zombie, bool spam, bool dying, u64 *len) +static bool append_msg(int fd, const u8 *msg, u32 timestamp, u64 *len) { struct gossip_hdr hdr; u32 msglen; @@ -81,12 +73,6 @@ static bool append_msg(int fd, const u8 *msg, u32 timestamp, msglen = tal_count(msg); hdr.len = cpu_to_be16(msglen); hdr.flags = 0; - if (spam) - hdr.flags |= CPU_TO_BE16(GOSSIP_STORE_RATELIMIT_BIT); - if (zombie) - hdr.flags |= CPU_TO_BE16(GOSSIP_STORE_ZOMBIE_BIT); - if (dying) - hdr.flags |= CPU_TO_BE16(GOSSIP_STORE_DYING_BIT); hdr.crc = cpu_to_be32(crc32c(timestamp, msg, msglen)); hdr.timestamp = cpu_to_be32(timestamp); @@ -107,16 +93,17 @@ static bool append_msg(int fd, const u8 *msg, u32 timestamp, * v11 mandated channel_updates use the htlc_maximum_msat field * v12 added the zombie flag for expired channel updates * v13 removed private gossip entries + * v14 removed zombie and spam flags */ static bool can_upgrade(u8 oldversion) { - return oldversion >= 9 && oldversion <= 12; + return oldversion >= 9 && oldversion <= 13; } /* On upgrade, do best effort on private channels: hand them to * lightningd as if we just receive them, before removing from the * store */ -static void give_lightningd_canned_private_update(struct routing_state *rstate, +static void give_lightningd_canned_private_update(struct daemon *daemon, const u8 *msg) { u8 *update; @@ -152,7 +139,7 @@ static void give_lightningd_canned_private_update(struct routing_state *rstate, } /* From NULL source (i.e. trust us!) */ - tell_lightningd_peer_update(rstate, + tell_lightningd_peer_update(daemon, NULL, short_channel_id, fee_base_msat, @@ -163,7 +150,8 @@ static void give_lightningd_canned_private_update(struct routing_state *rstate, } static bool upgrade_field(u8 oldversion, - struct routing_state *rstate, + struct daemon *daemon, + u16 hdr_flags, u8 **msg) { int type = fromwire_peektype(*msg); @@ -181,7 +169,13 @@ static bool upgrade_field(u8 oldversion, if (type == WIRE_GOSSIP_STORE_PRIVATE_CHANNEL_OBS) { *msg = tal_free(*msg); } else if (type == WIRE_GOSSIP_STORE_PRIVATE_UPDATE_OBS) { - give_lightningd_canned_private_update(rstate, *msg); + give_lightningd_canned_private_update(daemon, *msg); + *msg = tal_free(*msg); + } + } + if (oldversion <= 13) { + /* Discard any zombies */ + if (hdr_flags & GOSSIP_STORE_ZOMBIE_BIT_V13) { *msg = tal_free(*msg); } } @@ -189,60 +183,88 @@ static bool upgrade_field(u8 oldversion, return true; } -/* Read gossip store entries, copy non-deleted ones. This code is written - * as simply and robustly as possible! */ -static u32 gossip_store_compact_offline(struct routing_state *rstate) +/* Read gossip store entries, copy non-deleted ones. Check basic + * validity, but this code is written as simply and robustly as + * possible! + * + * Returns fd of new store. + */ +static int gossip_store_compact(struct daemon *daemon, + u64 *total_len, + bool *populated, + struct chan_dying **dying) { - size_t count = 0, deleted = 0; + size_t cannounces = 0, cupdates = 0, nannounces = 0, deleted = 0; int old_fd, new_fd; - u64 oldlen, newlen; + u64 old_len, cur_off; struct gossip_hdr hdr; u8 oldversion, version = GOSSIP_STORE_VER; struct stat st; + bool prev_chan_ann = false; + struct timeabs start = time_now(); + const char *bad; + + *populated = false; + old_len = 0; + + new_fd = open(GOSSIP_STORE_TEMP_FILENAME, O_RDWR|O_TRUNC|O_CREAT, 0600); + if (new_fd < 0) { + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Opening new gossip_store file: %s", + strerror(errno)); + } + + if (!write_all(new_fd, &version, sizeof(version))) { + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Writing new gossip_store file: %s", + strerror(errno)); + } + *total_len = sizeof(version); + /* RDWR since we add closed marker at end! */ old_fd = open(GOSSIP_STORE_FILENAME, O_RDWR); - if (old_fd == -1) - return 0; + if (old_fd == -1) { + if (errno == ENOENT) + goto rename_new; + + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Reading gossip_store file: %s", + strerror(errno)); + }; if (fstat(old_fd, &st) != 0) { status_broken("Could not stat gossip_store: %s", strerror(errno)); - goto close_old; - } - - new_fd = open(GOSSIP_STORE_TEMP_FILENAME, O_RDWR|O_TRUNC|O_CREAT, 0600); - if (new_fd < 0) { - status_broken( - "Could not open file for gossip_store compaction"); - goto close_old; + goto rename_new; } if (!read_all(old_fd, &oldversion, sizeof(oldversion)) || (oldversion != version && !can_upgrade(oldversion))) { status_broken("gossip_store_compact: bad version"); - goto close_and_delete; + goto rename_new; } - if (!write_all(new_fd, &version, sizeof(version))) { - status_broken("gossip_store_compact_offline: writing version to store: %s", - strerror(errno)); - goto close_and_delete; - } + cur_off = old_len = sizeof(oldversion); - /* Read everything, write non-deleted ones to new_fd */ + /* Read everything, write non-deleted ones to new_fd. If something goes wrong, + * we end up with truncated store. */ while (read_all(old_fd, &hdr, sizeof(hdr))) { size_t msglen; u8 *msg; + /* Partial writes can happen, and we simply truncate */ msglen = be16_to_cpu(hdr.len); msg = tal_arr(NULL, u8, msglen); if (!read_all(old_fd, msg, msglen)) { - status_broken("gossip_store_compact_offline: reading msg len %zu from store: %s", - msglen, strerror(errno)); + status_unusual("gossip_store_compact: store ends early at %"PRIu64, + old_len); tal_free(msg); - goto close_and_delete; + goto rename_new; } + cur_off = old_len; + old_len += sizeof(hdr) + msglen; + if (be16_to_cpu(hdr.flags) & GOSSIP_STORE_DELETED_BIT) { deleted++; tal_free(msg); @@ -252,17 +274,18 @@ static u32 gossip_store_compact_offline(struct routing_state *rstate) /* Check checksum (upgrade would overwrite, so do it now) */ if (be32_to_cpu(hdr.crc) != crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)) { - status_broken("gossip_store_compact_offline: checksum verification failed? %08x should be %08x", + bad = tal_fmt(tmpctx, "checksum verification failed? %08x should be %08x", be32_to_cpu(hdr.crc), crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)); - tal_free(msg); - goto close_and_delete; + goto badmsg; } if (oldversion != version) { - if (!upgrade_field(oldversion, rstate, &msg)) { + if (!upgrade_field(oldversion, daemon, + be16_to_cpu(hdr.flags), &msg)) { tal_free(msg); - goto close_and_delete; + bad = "upgrade of store failed"; + goto badmsg; } /* It can tell us to delete record entirely. */ @@ -285,708 +308,281 @@ static u32 gossip_store_compact_offline(struct routing_state *rstate) continue; } + switch (fromwire_peektype(msg)) { + case WIRE_GOSSIP_STORE_CHANNEL_AMOUNT: + /* Previous channel_announcement may have been deleted */ + if (prev_chan_ann) + cannounces++; + prev_chan_ann = false; + break; + case WIRE_CHANNEL_ANNOUNCEMENT: + if (prev_chan_ann) { + bad = "channel_announcement without amount"; + goto badmsg; + } + prev_chan_ann = true; + break; + case WIRE_GOSSIP_STORE_CHAN_DYING: { + struct chan_dying cd; + + if (!fromwire_gossip_store_chan_dying(msg, + &cd.scid, + &cd.deadline)) { + bad = "Bad gossip_store_chan_dying"; + goto badmsg; + } + /* By convention, these offsets are *after* header */ + cd.gossmap_offset = *total_len + sizeof(hdr); + tal_arr_expand(dying, cd); + break; + } + case WIRE_CHANNEL_UPDATE: + cupdates++; + break; + case WIRE_NODE_ANNOUNCEMENT: + nannounces++; + break; + default: + bad = "Unknown message"; + goto badmsg; + } + if (!write_all(new_fd, &hdr, sizeof(hdr)) || !write_all(new_fd, msg, msglen)) { - status_broken("gossip_store_compact_offline: writing msg len %zu to new store: %s", + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "gossip_store_compact: writing msg len %zu to new store: %s", msglen, strerror(errno)); - tal_free(msg); - goto close_and_delete; } tal_free(msg); - count++; - } - if (close(new_fd) != 0) { - status_broken("gossip_store_compact_offline: closing new store: %s", - strerror(errno)); - goto close_old; - } - if (rename(GOSSIP_STORE_TEMP_FILENAME, GOSSIP_STORE_FILENAME) != 0) { - status_broken("gossip_store_compact_offline: rename failed: %s", - strerror(errno)); + *total_len += sizeof(hdr) + msglen; } - /* Create end marker now new file exists. */ - oldlen = lseek(old_fd, SEEK_END, 0); - newlen = lseek(new_fd, SEEK_END, 0); - append_msg(old_fd, towire_gossip_store_ended(tmpctx, newlen), - 0, false, false, false, &oldlen); - close(old_fd); - status_debug("gossip_store_compact_offline: %zu deleted, %zu copied", - deleted, count); - return st.st_mtime; - -close_and_delete: - close(new_fd); -close_old: - close(old_fd); - unlink(GOSSIP_STORE_TEMP_FILENAME); - return 0; -} - -struct gossip_store *gossip_store_new(struct routing_state *rstate) -{ - struct gossip_store *gs = tal(rstate, struct gossip_store); - gs->count = gs->deleted = 0; - gs->writable = true; - gs->timestamp = gossip_store_compact_offline(rstate); - gs->fd = open(GOSSIP_STORE_FILENAME, O_RDWR|O_CREAT, 0600); - if (gs->fd < 0) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Opening gossip_store store: %s", - strerror(errno)); - gs->rstate = rstate; - gs->disable_compaction = false; - gs->len = sizeof(gs->version); + assert(*total_len == lseek(new_fd, 0, SEEK_END)); - tal_add_destructor(gs, gossip_store_destroy); + /* Unlikely, but a channel_announcement without an amount: we just truncate. */ + if (prev_chan_ann) { + bad = "channel_announcement without amount"; + goto badmsg; + } - /* Try to read the version, write it if this is a new file, or truncate - * if the version doesn't match */ - if (read(gs->fd, &gs->version, sizeof(gs->version)) - == sizeof(gs->version)) { - /* Version match? All good */ - if (gs->version == GOSSIP_STORE_VER) - return gs; - - status_unusual("Gossip store version %u not %u: removing", - gs->version, GOSSIP_STORE_VER); - if (ftruncate(gs->fd, 0) != 0) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Truncating store: %s", strerror(errno)); - /* Subtle: we are at offset 1, move back to start! */ - if (lseek(gs->fd, 0, SEEK_SET) != 0) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Seeking to start of store: %s", - strerror(errno)); + /* If we have any contents, and the file is less than 1 hour + * old, say "seems good" */ + if (st.st_mtime > time_now().ts.tv_sec - 3600 && *total_len > 1) { + *populated = true; } - /* Empty file, write version byte */ - gs->version = GOSSIP_STORE_VER; - if (write(gs->fd, &gs->version, sizeof(gs->version)) - != sizeof(gs->version)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Writing version to store: %s", strerror(errno)); - return gs; -} -/* Returns bytes transferred, or 0 on error */ -static size_t transfer_store_msg(int from_fd, size_t from_off, - int to_fd, size_t to_off, - int *type) -{ - struct gossip_hdr hdr; - u16 flags, msglen; - u8 *msg; - const u8 *p; - size_t tmplen; - - *type = -1; - if (pread(from_fd, &hdr, sizeof(hdr), from_off) != sizeof(hdr)) { - status_broken("Failed reading header from to gossip store @%zu" - ": %s", - from_off, strerror(errno)); - return 0; +rename_new: + if (rename(GOSSIP_STORE_TEMP_FILENAME, GOSSIP_STORE_FILENAME) != 0) { + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "gossip_store_compact: rename failed: %s", + strerror(errno)); } - flags = be16_to_cpu(hdr.flags); - if (flags & GOSSIP_STORE_DELETED_BIT) { - status_broken("Can't transfer deleted msg from gossip store @%zu", - from_off); - return 0; + /* Create end marker now new file exists. */ + if (old_fd != -1) { + append_msg(old_fd, towire_gossip_store_ended(tmpctx, *total_len), + 0, &old_len); + close(old_fd); } - msglen = be16_to_cpu(hdr.len); + status_debug("Store compact time: %"PRIu64" msec", + time_to_msec(time_between(time_now(), start))); + status_debug("gossip_store: Read %zu/%zu/%zu/%zu cannounce/cupdate/nannounce/delete from store in %"PRIu64" bytes, now %"PRIu64" bytes (populated=%s)", + cannounces, cupdates, nannounces, deleted, + old_len, *total_len, + *populated ? "true": "false"); + return new_fd; - /* FIXME: Reuse buffer? */ - msg = tal_arr(tmpctx, u8, sizeof(hdr) + msglen); - memcpy(msg, &hdr, sizeof(hdr)); - if (pread(from_fd, msg + sizeof(hdr), msglen, from_off + sizeof(hdr)) - != msglen) { - status_broken("Failed reading %u from to gossip store @%zu" - ": %s", - msglen, from_off, strerror(errno)); - return 0; - } +badmsg: + /* We truncate */ + status_broken("gossip_store: %s (offset %"PRIu64"). Moving to %s.corrupt and truncating", + bad, cur_off, GOSSIP_STORE_FILENAME); - if (pwrite(to_fd, msg, msglen + sizeof(hdr), to_off) - != msglen + sizeof(hdr)) { - status_broken("Failed writing to gossip store: %s", + rename(GOSSIP_STORE_FILENAME, GOSSIP_STORE_FILENAME ".corrupt"); + if (lseek(new_fd, 0, SEEK_SET) != 0 + || !write_all(new_fd, &version, sizeof(version))) { + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Overwriting new gossip_store file: %s", strerror(errno)); - return 0; } - - /* Can't use peektype here, since we have header on front */ - p = msg + sizeof(hdr); - tmplen = msglen; - *type = fromwire_u16(&p, &tmplen); - if (!p) - *type = -1; - tal_free(msg); - return sizeof(hdr) + msglen; + *total_len = sizeof(version); + goto rename_new; } -/* We keep a htable map of old gossip_store offsets to new ones. */ -struct offset_map { - size_t from, to; -}; - -static size_t offset_map_key(const struct offset_map *omap) +struct gossip_store *gossip_store_new(const tal_t *ctx, + struct daemon *daemon, + bool *populated, + struct chan_dying **dying) { - return omap->from; -} + struct gossip_store *gs = tal(ctx, struct gossip_store); -static size_t hash_offset(size_t from) -{ - /* Crappy fast hash is "good enough" */ - return (from >> 5) ^ from; + gs->daemon = daemon; + *dying = tal_arr(ctx, struct chan_dying, 0); + gs->fd = gossip_store_compact(daemon, &gs->len, populated, dying); + tal_add_destructor(gs, gossip_store_destroy); + return gs; } -static bool offset_map_eq(const struct offset_map *omap, const size_t from) +int gossip_store_get_fd(const struct gossip_store *gs) { - return omap->from == from; + return gs->fd; } -HTABLE_DEFINE_TYPE(struct offset_map, - offset_map_key, hash_offset, offset_map_eq, offmap); -static void move_broadcast(struct offmap *offmap, - struct broadcastable *bcast, - const char *what) +u64 gossip_store_add(struct gossip_store *gs, const u8 *gossip_msg, u32 timestamp) { - struct offset_map *omap; + u64 off = gs->len; - if (!bcast->index) - return; + if (!append_msg(gs->fd, gossip_msg, timestamp, &gs->len)) { + status_broken("Failed writing to gossip store: %s", + strerror(errno)); + return 0; + } - omap = offmap_get(offmap, bcast->index); - if (!omap) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Could not relocate %s at offset %u", - what, bcast->index); - bcast->index = omap->to; - offmap_del(offmap, omap); + /* By gossmap convention, offset is *after* hdr */ + return off + sizeof(struct gossip_hdr); } -/** - * Rewrite the on-disk gossip store, compacting it along the way - * - * Creates a new file, writes all the updates from the `broadcast_state`, and - * then atomically swaps the files. - */ -bool gossip_store_compact(struct gossip_store *gs) +/* Offsets are all gossmap-style: *after* hdr! */ +static const u8 *gossip_store_get_with_hdr(const tal_t *ctx, + struct gossip_store *gs, + u64 offset, + struct gossip_hdr *hdr) { - size_t count = 0, deleted = 0; - int fd; - u64 off, len = sizeof(gs->version), idx; - struct offmap *offmap; - struct gossip_hdr hdr; - struct offmap_iter oit; - struct node_map_iter nit; - struct offset_map *omap; - - if (gs->disable_compaction) - return false; - - status_debug( - "Compacting gossip_store with %zu entries, %zu of which are stale", - gs->count, gs->deleted); - - fd = open(GOSSIP_STORE_TEMP_FILENAME, O_RDWR|O_TRUNC|O_CREAT, 0600); - - if (fd < 0) { - status_broken( - "Could not open file for gossip_store compaction"); - goto disable; - } - - if (write(fd, &gs->version, sizeof(gs->version)) - != sizeof(gs->version)) { - status_broken("Writing version to store: %s", strerror(errno)); - goto unlink_disable; - } - - /* Walk old file, copy everything and remember new offsets. */ - offmap = tal(tmpctx, struct offmap); - offmap_init_sized(offmap, gs->count); - - /* Start by writing all channel announcements and updates. */ - off = 1; - while (pread(gs->fd, &hdr, sizeof(hdr), off) == sizeof(hdr)) { - u16 msglen; - u32 wlen; - int msgtype; - - msglen = be16_to_cpu(hdr.len); - if (be16_to_cpu(hdr.flags) & GOSSIP_STORE_DELETED_BIT) { - off += sizeof(hdr) + msglen; - deleted++; - continue; - } - - count++; - wlen = transfer_store_msg(gs->fd, off, fd, len, &msgtype); - if (wlen == 0) - goto unlink_disable; - - /* We track location of all these message types. */ - if (msgtype == WIRE_CHANNEL_ANNOUNCEMENT - || msgtype == WIRE_CHANNEL_UPDATE - || msgtype == WIRE_NODE_ANNOUNCEMENT) { - omap = tal(offmap, struct offset_map); - omap->from = off; - omap->to = len; - offmap_add(offmap, omap); - } - len += wlen; - off += wlen; - } - - /* OK, now we've written file successfully, we can move broadcasts. */ - /* Remap node announcements. */ - for (struct node *n = node_map_first(gs->rstate->nodes, &nit); - n; - n = node_map_next(gs->rstate->nodes, &nit)) { - move_broadcast(offmap, &n->bcast, "node_announce"); - } - - /* Remap channel announcements and updates */ - for (struct chan *c = uintmap_first(&gs->rstate->chanmap, &idx); - c; - c = uintmap_after(&gs->rstate->chanmap, &idx)) { - move_broadcast(offmap, &c->bcast, "channel_announce"); - move_broadcast(offmap, &c->half[0].bcast, "channel_update"); - move_broadcast(offmap, &c->half[1].bcast, "channel_update"); - } + u32 msglen, checksum; + u8 *msg; - /* That should be everything. */ - omap = offmap_first(offmap, &oit); - if (omap) + if (offset <= sizeof(*hdr)) status_failed(STATUS_FAIL_INTERNAL_ERROR, - "gossip_store: Entry at %zu->%zu not updated?", - omap->from, omap->to); - - if (count != gs->count - gs->deleted) + "gossip_store: can't access offset %"PRIu64, + offset); + if (pread(gs->fd, hdr, sizeof(*hdr), offset - sizeof(*hdr)) != sizeof(*hdr)) { status_failed(STATUS_FAIL_INTERNAL_ERROR, - "gossip_store: Expected %zu msgs in new" - " gossip store, got %zu", - gs->count - gs->deleted, count); + "gossip_store: can't read hdr offset %"PRIu64 + "/%"PRIu64": %s", + offset - sizeof(*hdr), gs->len, strerror(errno)); + } - if (deleted != gs->deleted) + if (be16_to_cpu(hdr->flags) & GOSSIP_STORE_DELETED_BIT) status_failed(STATUS_FAIL_INTERNAL_ERROR, - "gossip_store: Expected %zu deleted msgs in old" - " gossip store, got %zu", - gs->deleted, deleted); + "gossip_store: get delete entry offset %"PRIu64 + "/%"PRIu64"", + offset - sizeof(*hdr), gs->len); - if (rename(GOSSIP_STORE_TEMP_FILENAME, GOSSIP_STORE_FILENAME) == -1) + msglen = be16_to_cpu(hdr->len); + checksum = be32_to_cpu(hdr->crc); + msg = tal_arr(ctx, u8, msglen); + if (pread(gs->fd, msg, msglen, offset) != msglen) status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Error swapping compacted gossip_store into place:" - " %s", - strerror(errno)); - - status_debug( - "Compaction completed: dropped %zu messages, new count %zu, len %"PRIu64, - deleted, count, len); - - /* Write end marker now new one is ready */ - append_msg(gs->fd, towire_gossip_store_ended(tmpctx, len), - 0, false, false, false, &gs->len); - - gs->count = count; - gs->deleted = 0; - gs->len = len; - close(gs->fd); - gs->fd = fd; - - return true; - -unlink_disable: - unlink(GOSSIP_STORE_TEMP_FILENAME); -disable: - status_debug("Encountered an error while compacting, disabling " - "future compactions."); - gs->disable_compaction = true; - return false; -} - -u64 gossip_store_add(struct gossip_store *gs, const u8 *gossip_msg, - u32 timestamp, bool zombie, - bool spam, bool dying, const u8 *addendum) -{ - u64 off = gs->len; - - /* Should never get here during loading! */ - assert(gs->writable); + "gossip_store: can't read len %u offset %"PRIu64 + "/%"PRIu64, msglen, offset, gs->len); - if (!append_msg(gs->fd, gossip_msg, timestamp, zombie, spam, dying, &gs->len)) { - status_broken("Failed writing to gossip store: %s", - strerror(errno)); - return 0; - } - if (addendum && !append_msg(gs->fd, addendum, 0, false, false, false, &gs->len)) { - status_broken("Failed writing addendum to gossip store: %s", - strerror(errno)); - return 0; - } + if (checksum != crc32c(be32_to_cpu(hdr->timestamp), msg, msglen)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "gossip_store: bad checksum offset %"PRIu64": %s", + offset - sizeof(*hdr), tal_hex(tmpctx, msg)); - gs->count++; - if (addendum) - gs->count++; - return off; + return msg; } -void gossip_store_mark_dying(struct gossip_store *gs, - const struct broadcastable *bcast, - int type) +static bool check_msg_type(struct gossip_store *gs, u64 offset, int flag, int type) { - const u8 *msg; - be16 flags; - - /* Should never get here during loading! */ - assert(gs->writable); - - /* Should never try to overwrite version */ - assert(bcast->index); - - /* Sanity check, that this is a channel announcement */ - msg = gossip_store_get(tmpctx, gs, bcast->index); - if (fromwire_peektype(msg) != type) { - status_broken("gossip_store incorrect dying msg not %u @%u of %"PRIu64": %s", - type, bcast->index, gs->len, tal_hex(tmpctx, msg)); - return; - } + struct gossip_hdr hdr; + const u8 *msg = gossip_store_get_with_hdr(tmpctx, gs, offset, &hdr); - if (pread(gs->fd, &flags, sizeof(flags), bcast->index) != sizeof(flags)) { - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Could not read to mark dying at %u/%"PRIu64": %s", - bcast->index, gs->len, strerror(errno)); - } + if (fromwire_peektype(msg) == type) + return true; - flags |= cpu_to_be16(GOSSIP_STORE_DYING_BIT); - if (pwrite(gs->fd, &flags, sizeof(flags), bcast->index) != sizeof(flags)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Failed writing flags to dying @%u: %s", - bcast->index, strerror(errno)); + status_broken("asked to flag-%u type %i @%"PRIu64" but store contains " + "%i (gs->len=%"PRIu64"): %s", + flag, type, offset, fromwire_peektype(msg), + gs->len, tal_hex(tmpctx, msg)); + return false; } -/* Returns index of following entry. */ -static u32 delete_by_index(struct gossip_store *gs, u32 index, int type) +/* Returns offset of following entry (i.e. after its header). */ +u64 gossip_store_set_flag(struct gossip_store *gs, + u64 offset, u16 flag, int type) { struct { beint16_t beflags; beint16_t belen; } hdr; - - /* Should never get here during loading! */ - assert(gs->writable); + u64 hdr_off = offset - sizeof(struct gossip_hdr); /* Should never try to overwrite version */ - assert(index); + assert(offset > sizeof(struct gossip_hdr)); /* FIXME: debugging a gs->len overrun issue reported in #6270 */ - if (pread(gs->fd, &hdr, sizeof(hdr), index) != sizeof(hdr)) { - status_broken("gossip_store overrun during delete @%u type: %i" - " gs->len: %"PRIu64, index, type, gs->len); - return index; + if (pread(gs->fd, &hdr, sizeof(hdr), hdr_off) != sizeof(hdr)) { + status_broken("gossip_store pread fail during flag %u @%"PRIu64" type: %i" + " gs->len: %"PRIu64, flag, hdr_off, type, gs->len); + return offset; } - if (index + sizeof(struct gossip_hdr) + - be16_to_cpu(hdr.belen) > gs->len) { - status_broken("gossip_store overrun during delete @%u type: %i" - " gs->len: %"PRIu64, index, type, gs->len); - return index; + if (offset + be16_to_cpu(hdr.belen) > gs->len) { + status_broken("gossip_store overrun during flag-%u @%"PRIu64" type: %i" + " gs->len: %"PRIu64, flag, hdr_off, type, gs->len); + return offset; } - const u8 *msg = gossip_store_get(tmpctx, gs, index); - if(fromwire_peektype(msg) != type) { - status_broken("asked to delete type %i @%u but store contains " - "%i (gs->len=%"PRIu64"): %s", - type, index, fromwire_peektype(msg), - gs->len, tal_hex(tmpctx, msg)); - return index; - } + if (!check_msg_type(gs, offset, flag, type)) + return offset; - assert((be16_to_cpu(hdr.beflags) & GOSSIP_STORE_DELETED_BIT) == 0); - hdr.beflags |= cpu_to_be16(GOSSIP_STORE_DELETED_BIT); - if (pwrite(gs->fd, &hdr.beflags, sizeof(hdr.beflags), index) != sizeof(hdr.beflags)) + assert((be16_to_cpu(hdr.beflags) & flag) == 0); + hdr.beflags |= cpu_to_be16(flag); + if (pwrite(gs->fd, &hdr.beflags, sizeof(hdr.beflags), hdr_off) != sizeof(hdr.beflags)) status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Failed writing flags to delete @%u: %s", - index, strerror(errno)); - gs->deleted++; + "Failed writing flags to delete @%"PRIu64": %s", + offset, strerror(errno)); - return index + sizeof(struct gossip_hdr) + be16_to_cpu(hdr.belen); + return offset + be16_to_cpu(hdr.belen) + sizeof(struct gossip_hdr); } -void gossip_store_delete(struct gossip_store *gs, - struct broadcastable *bcast, - int type) +void gossip_store_del(struct gossip_store *gs, + u64 offset, + int type) { u32 next_index; - if (!bcast->index) - return; - - next_index = delete_by_index(gs, bcast->index, type); - - /* Reset index. */ - bcast->index = 0; + assert(offset > sizeof(struct gossip_hdr)); + next_index = gossip_store_set_flag(gs, offset, + GOSSIP_STORE_DELETED_BIT, + type); /* For a channel_announcement, we need to delete amount too */ if (type == WIRE_CHANNEL_ANNOUNCEMENT) - delete_by_index(gs, next_index, - WIRE_GOSSIP_STORE_CHANNEL_AMOUNT); -} - -void gossip_store_mark_channel_deleted(struct gossip_store *gs, - const struct short_channel_id *scid) -{ - gossip_store_add(gs, towire_gossip_store_delete_chan(tmpctx, scid), - 0, false, false, false, NULL); -} - -static void mark_zombie(struct gossip_store *gs, - const struct broadcastable *bcast, - enum peer_wire expected_type) -{ - beint16_t beflags; - u32 index = bcast->index; - - /* We assume flags is the first field! */ - BUILD_ASSERT(offsetof(struct gossip_hdr, flags) == 0); - - /* Should never get here during loading! */ - assert(gs->writable); - assert(index); - - const u8 *msg = gossip_store_get(tmpctx, gs, index); - assert(fromwire_peektype(msg) == expected_type); - - if (pread(gs->fd, &beflags, sizeof(beflags), index) != sizeof(beflags)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Failed reading flags to zombie %s @%u: %s", - peer_wire_name(expected_type), - index, strerror(errno)); - - assert((be16_to_cpu(beflags) & GOSSIP_STORE_DELETED_BIT) == 0); - beflags |= cpu_to_be16(GOSSIP_STORE_ZOMBIE_BIT); - if (pwrite(gs->fd, &beflags, sizeof(beflags), index) != sizeof(beflags)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Failed writing flags to zombie %s @%u: %s", - peer_wire_name(expected_type), - index, strerror(errno)); + gossip_store_set_flag(gs, next_index, + GOSSIP_STORE_DELETED_BIT, + WIRE_GOSSIP_STORE_CHANNEL_AMOUNT); } -/* Marks the length field of a channel_announcement with the zombie flag bit */ -void gossip_store_mark_channel_zombie(struct gossip_store *gs, - struct broadcastable *bcast) -{ - mark_zombie(gs, bcast, WIRE_CHANNEL_ANNOUNCEMENT); -} - -/* Marks the length field of a channel_update with the zombie flag bit */ -void gossip_store_mark_cupdate_zombie(struct gossip_store *gs, - struct broadcastable *bcast) -{ - mark_zombie(gs, bcast, WIRE_CHANNEL_UPDATE); -} - -const u8 *gossip_store_get(const tal_t *ctx, - struct gossip_store *gs, - u64 offset) +u32 gossip_store_get_timestamp(struct gossip_store *gs, u64 offset) { struct gossip_hdr hdr; - u32 msglen, checksum; - u8 *msg; - if (offset == 0) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "gossip_store: can't access offset %"PRIu64, - offset); - if (pread(gs->fd, &hdr, sizeof(hdr), offset) != sizeof(hdr)) { - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "gossip_store: can't read hdr offset %"PRIu64 - "/%"PRIu64": %s", - offset, gs->len, strerror(errno)); - } - - if (be16_to_cpu(hdr.flags) & GOSSIP_STORE_DELETED_BIT) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "gossip_store: get delete entry offset %"PRIu64 - "/%"PRIu64"", - offset, gs->len); + assert(offset > sizeof(struct gossip_hdr)); - msglen = be16_to_cpu(hdr.len); - checksum = be32_to_cpu(hdr.crc); - msg = tal_arr(ctx, u8, msglen); - if (pread(gs->fd, msg, msglen, offset + sizeof(hdr)) != msglen) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "gossip_store: can't read len %u offset %"PRIu64 - "/%"PRIu64, msglen, offset, gs->len); - - if (checksum != crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "gossip_store: bad checksum offset %"PRIu64": %s", - offset, tal_hex(tmpctx, msg)); - - return msg; -} - -int gossip_store_readonly_fd(struct gossip_store *gs) -{ - int fd = open(GOSSIP_STORE_FILENAME, O_RDONLY); - - /* Skip over version header */ - if (fd != -1 && lseek(fd, 1, SEEK_SET) != 1) { - close_noerr(fd); - fd = -1; + if (pread(gs->fd, &hdr, sizeof(hdr), offset - sizeof(hdr)) != sizeof(hdr)) { + status_broken("gossip_store overrun during get_timestamp @%"PRIu64 + " gs->len: %"PRIu64, offset, gs->len); + return 0; } - return fd; + + return be32_to_cpu(hdr.timestamp); } -u32 gossip_store_load(struct routing_state *rstate, struct gossip_store *gs) +void gossip_store_set_timestamp(struct gossip_store *gs, u64 offset, u32 timestamp) { struct gossip_hdr hdr; - u32 msglen, checksum; - u8 *msg; - struct amount_sat satoshis; - const char *bad; - size_t stats[] = {0, 0, 0, 0}; - struct timeabs start = time_now(); - u8 *chan_ann = NULL; - u64 chan_ann_off = 0; /* Spurious gcc-9 (Ubuntu 9-20190402-1ubuntu1) 9.0.1 20190402 (experimental) warning */ - - gs->writable = false; - while (pread(gs->fd, &hdr, sizeof(hdr), gs->len) == sizeof(hdr)) { - bool spam; - - msglen = be16_to_cpu(hdr.len); - checksum = be32_to_cpu(hdr.crc); - msg = tal_arr(tmpctx, u8, msglen); - - if (pread(gs->fd, msg, msglen, gs->len+sizeof(hdr)) != msglen) { - bad = "gossip_store: truncated file?"; - goto corrupt; - } - - if (checksum != crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)) { - bad = tal_fmt(tmpctx, "Checksum verification failed: %08x should be %08x", - checksum, crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)); - goto badmsg; - } - - /* Skip deleted entries */ - if (be16_to_cpu(hdr.flags) & GOSSIP_STORE_DELETED_BIT) { - /* Count includes deleted! */ - gs->count++; - gs->deleted++; - goto next; - } - spam = (be16_to_cpu(hdr.flags) & GOSSIP_STORE_RATELIMIT_BIT); - - switch (fromwire_peektype(msg)) { - case WIRE_GOSSIP_STORE_CHANNEL_AMOUNT: - if (!fromwire_gossip_store_channel_amount(msg, - &satoshis)) { - bad = "Bad gossip_store_channel_amount"; - goto badmsg; - } - /* Previous channel_announcement may have been deleted */ - if (!chan_ann) - break; - if (!routing_add_channel_announcement(rstate, - take(chan_ann), - satoshis, - chan_ann_off, - NULL)) { - bad = "Bad channel_announcement"; - goto badmsg; - } - chan_ann = NULL; - stats[0]++; - break; - case WIRE_CHANNEL_ANNOUNCEMENT: - if (chan_ann) { - bad = "channel_announcement without amount"; - goto badmsg; - } - /* Save for channel_amount (next msg) */ - chan_ann = tal_steal(gs, msg); - chan_ann_off = gs->len; - break; - case WIRE_GOSSIP_STORE_CHAN_DYING: { - struct short_channel_id scid; - u32 deadline; - - if (!fromwire_gossip_store_chan_dying(msg, &scid, &deadline)) { - bad = "Bad gossip_store_chan_dying"; - goto badmsg; - } - remember_chan_dying(rstate, &scid, deadline, gs->len); - break; - } - case WIRE_CHANNEL_UPDATE: - if (!routing_add_channel_update(rstate, - take(msg), gs->len, - NULL, false, - spam, false)) { - bad = "Bad channel_update"; - goto badmsg; - } - stats[1]++; - break; - case WIRE_NODE_ANNOUNCEMENT: - if (!routing_add_node_announcement(rstate, - take(msg), gs->len, - NULL, NULL, spam)) { - /* FIXME: This has been reported: routing.c - * has logged, so ignore. */ - break; - } - stats[2]++; - break; - default: - bad = "Unknown message"; - goto badmsg; - } - - gs->count++; - next: - gs->len += sizeof(hdr) + msglen; - clean_tmpctx(); - } - - if (chan_ann) { - bad = "dangling channel_announcement"; - goto corrupt; - } - - bad = unfinalized_entries(tmpctx, rstate); - if (bad) - goto corrupt; + const u8 *msg; - goto out; + msg = gossip_store_get_with_hdr(tmpctx, gs, offset, &hdr); -badmsg: - bad = tal_fmt(tmpctx, "%s (%s)", bad, tal_hex(tmpctx, msg)); - -corrupt: - status_broken("gossip_store: %s. Moving to %s.corrupt and truncating", - bad, GOSSIP_STORE_FILENAME); + /* Change timestamp and crc */ + hdr.timestamp = cpu_to_be32(timestamp); + hdr.crc = cpu_to_be32(crc32c(timestamp, msg, tal_bytelen(msg))); - /* FIXME: Debug partial truncate case. */ - rename(GOSSIP_STORE_FILENAME, GOSSIP_STORE_FILENAME ".corrupt"); - close(gs->fd); - gs->fd = open(GOSSIP_STORE_FILENAME, O_RDWR|O_TRUNC|O_CREAT, 0600); - if (gs->fd < 0 || !write_all(gs->fd, &gs->version, sizeof(gs->version))) + if (pwrite(gs->fd, &hdr, sizeof(hdr), offset - sizeof(hdr)) != sizeof(hdr)) status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Truncating new store file: %s", strerror(errno)); - remove_all_gossip(rstate); - gs->count = gs->deleted = 0; - gs->len = 1; - gs->timestamp = 0; -out: - gs->writable = true; - status_debug("total store load time: %"PRIu64" msec", - time_to_msec(time_between(time_now(), start))); - status_debug("gossip_store: Read %zu/%zu/%zu/%zu cannounce/cupdate/nannounce/cdelete from store (%zu deleted) in %"PRIu64" bytes", - stats[0], stats[1], stats[2], stats[3], gs->deleted, - gs->len); - - return gs->timestamp; + "Failed writing header to re-timestamp @%"PRIu64": %s", + offset, strerror(errno)); } diff --git a/gossipd/gossip_store.h b/gossipd/gossip_store.h index b318842f150a..374a7181c22f 100644 --- a/gossipd/gossip_store.h +++ b/gossipd/gossip_store.h @@ -7,99 +7,92 @@ #include #include #include -#include /** * gossip_store -- On-disk storage related information */ struct gossip_store; -struct routing_state; +struct daemon; + +/* For channels we find dying in the store, on load. They are < 12 + * blocks closed. */ +struct chan_dying { + struct short_channel_id scid; + /* Offset of dying marker in the gossip_store */ + u64 gossmap_offset; + /* Blockheight where it's supposed to be deleted. */ + u32 deadline; +}; -struct gossip_store *gossip_store_new(struct routing_state *rstate); +/** + * Load the gossip_store + * @ctx: the context to allocate from + * @daemon: the daemon context + * @populated: set to false if store is empty/obviously partial. + * @dying: an array of channels we found dying markers for. + */ +struct gossip_store *gossip_store_new(const tal_t *ctx, + struct daemon *daemon, + bool *populated, + struct chan_dying **dying); /** - * Load the initial gossip store, if any. + * Get the fd from the gossip_store. + * @gs: the gossip_store. * - * @param rstate The routing state to load init. - * @param gs The `gossip_store` to read from - * - * Returns the last-modified time of the store, or 0 if it was created new. + * Used by gossmap_manage to create a gossmap. + */ +int gossip_store_get_fd(const struct gossip_store *gs); + +/** + * Get the (refreshed!) gossmap from the gossip_store. + * @gs: gossip store */ -u32 gossip_store_load(struct routing_state *rstate, struct gossip_store *gs); +struct gossmap *gossip_store_gossmap(struct gossip_store *gs); /** - * Add a gossip message to the gossip_store (and optional addendum) + * Append a gossip message to the gossip_store * @gs: gossip store * @gossip_msg: the gossip message to insert. * @timestamp: the timestamp for filtering of this messsage. - * @zombie: true if this channel is missing a current channel_update. - * @spam: true if this message is rate-limited and squelched to peers. - * @dying: true if this message is for a dying channel. - * @addendum: another message to append immediately after this - * (for appending amounts to channel_announcements for internal use). */ -u64 gossip_store_add(struct gossip_store *gs, const u8 *gossip_msg, - u32 timestamp, bool zombie, bool spam, bool dying, - const u8 *addendum); +u64 gossip_store_add(struct gossip_store *gs, + const u8 *gossip_msg, + u32 timestamp); /** - * Delete the broadcast associated with this (if any). + * Delete the record at this offset (offset is that of + * record, not header, unlike bcast->index!). * * In developer mode, checks that type is correct. */ -void gossip_store_delete(struct gossip_store *gs, - struct broadcastable *bcast, - int type); - -/** - * Mark that the channel is about to be deleted, for convenience of - * others mapping the gossip_store. - */ -void gossip_store_mark_channel_deleted(struct gossip_store *gs, - const struct short_channel_id *scid); - -/* - * Marks the length field of a channel announcement with a zombie flag bit. - * This allows the channel_announcement to be retained in the store while - * waiting for channel updates to reactivate it. - */ -void gossip_store_mark_channel_zombie(struct gossip_store *gs, - struct broadcastable *bcast); - -void gossip_store_mark_cupdate_zombie(struct gossip_store *gs, - struct broadcastable *bcast); +void gossip_store_del(struct gossip_store *gs, + u64 offset, + int type); /** - * Mark this channel_announcement/channel_update as dying. + * Add a flag the record at this offset (offset is that of + * record!). Returns offset of *next* record. * - * We'll clean it up in 12 blocks, but this tells connectd not to gossip - * about it. + * In developer mode, checks that type is correct. */ -void gossip_store_mark_dying(struct gossip_store *gs, - const struct broadcastable *bcast, - int type); - +u64 gossip_store_set_flag(struct gossip_store *gs, + u64 offset, u16 flag, int type); /** - * Direct store accessor: loads gossip msg back from store. + * Direct store accessor: get timestamp header for a record. * - * Caller must ensure offset != 0. Never returns NULL. + * Offset is *after* the header. */ -const u8 *gossip_store_get(const tal_t *ctx, - struct gossip_store *gs, - u64 offset); - -/* Exposed for dev-compact-gossip-store to force compaction. */ -bool gossip_store_compact(struct gossip_store *gs); +u32 gossip_store_get_timestamp(struct gossip_store *gs, u64 offset); /** - * Get a readonly fd for the gossip_store. - * @gs: the gossip store. + * Direct store accessor: set timestamp header for a record. * - * Returns -1 on failure, and sets errno. + * Offset is *after* the header. */ -int gossip_store_readonly_fd(struct gossip_store *gs); +void gossip_store_set_timestamp(struct gossip_store *gs, u64 offset, u32 timestamp); #endif /* LIGHTNING_GOSSIPD_GOSSIP_STORE_H */ diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index af6ec048c1ec..778e48cd696e 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -26,12 +26,13 @@ #include #include #include +#include #include #include #include #include +#include #include -#include #include #include @@ -86,232 +87,22 @@ void peer_supplied_good_gossip(struct daemon *daemon, /* Queue a gossip message for the peer: connectd simply forwards it to * the peer. */ -void queue_peer_msg(struct peer *peer, const u8 *msg TAKES) +void queue_peer_msg(struct daemon *daemon, + const struct node_id *peer, + const u8 *msg TAKES) { - u8 *outermsg = towire_gossipd_send_gossip(NULL, &peer->id, msg); - daemon_conn_send(peer->daemon->connectd, take(outermsg)); + u8 *outermsg = towire_gossipd_send_gossip(NULL, peer, msg); + daemon_conn_send(daemon->connectd, take(outermsg)); if (taken(msg)) tal_free(msg); } -/*~ We have a helper for messages from the store. */ -void queue_peer_from_store(struct peer *peer, - const struct broadcastable *bcast) -{ - struct gossip_store *gs = peer->daemon->rstate->gs; - queue_peer_msg(peer, take(gossip_store_get(NULL, gs, bcast->index))); -} - -/*~ We don't actually keep node_announcements in memory; we keep them in - * a file called `gossip_store`. If we need some node details, we reload - * and reparse. It's slow, but generally rare. */ -static bool get_node_announcement(const tal_t *ctx, - struct daemon *daemon, - const struct node *n, - u8 rgb_color[3], - u8 alias[32], - u8 **features, - struct wireaddr **wireaddrs, - struct lease_rates **rates) -{ - const u8 *msg; - struct node_id id; - secp256k1_ecdsa_signature signature; - u32 timestamp; - u8 *addresses; - struct tlv_node_ann_tlvs *na_tlvs; - - if (!n->bcast.index) - return false; - - msg = gossip_store_get(tmpctx, daemon->rstate->gs, n->bcast.index); - - /* Note: validity of node_id is already checked. */ - if (!fromwire_node_announcement(ctx, msg, - &signature, features, - ×tamp, - &id, rgb_color, alias, - &addresses, - &na_tlvs)) { - status_broken("Bad local node_announcement @%u: %s", - n->bcast.index, tal_hex(tmpctx, msg)); - return false; - } - - if (!node_id_eq(&id, &n->id) || timestamp != n->bcast.timestamp) { - status_broken("Wrong node_announcement @%u:" - " expected %s timestamp %u " - " got %s timestamp %u", - n->bcast.index, - type_to_string(tmpctx, struct node_id, &n->id), - timestamp, - type_to_string(tmpctx, struct node_id, &id), - n->bcast.timestamp); - return false; - } - - *wireaddrs = fromwire_wireaddr_array(ctx, addresses); - *rates = tal_steal(ctx, na_tlvs->option_will_fund); - - tal_free(addresses); - return true; -} - -/* Version which also does nodeid lookup */ -static bool get_node_announcement_by_id(const tal_t *ctx, - struct daemon *daemon, - const struct node_id *node_id, - u8 rgb_color[3], - u8 alias[32], - u8 **features, - struct wireaddr **wireaddrs, - struct lease_rates **rates) -{ - struct node *n = get_node(daemon->rstate, node_id); - if (!n) - return false; - - return get_node_announcement(ctx, daemon, n, rgb_color, alias, - features, wireaddrs, rates); -} - -/*~Routines to handle gossip messages from peer, forwarded by subdaemons. +/*~Routines to handle gossip messages from peer, forwarded by connectd. *----------------------------------------------------------------------- * - * It's not the subdaemon's fault if they're malformed or invalid; so these - * all return an error packet which gets sent back to the subdaemon in that - * case. - */ - -/* The routing code checks that it's basically valid, returning an - * error message for the peer or NULL. NULL means it's OK, but the - * message might be redundant, in which case scid is also NULL. - * Otherwise `scid` gives us the short_channel_id claimed by the - * message, and puts the announcemnt on an internal 'pending' - * queue. We'll send a request to lightningd to look it up, and continue - * processing in `handle_txout_reply`. */ -static const u8 *handle_channel_announcement_msg(struct daemon *daemon, - const struct node_id *source_peer, - const u8 *msg) -{ - const struct short_channel_id *scid; - const u8 *err; - - /* If it's OK, tells us the short_channel_id to lookup; it notes - * if this is the unknown channel the peer was looking for (in - * which case, it frees and NULLs that ptr) */ - err = handle_channel_announcement(daemon->rstate, msg, - daemon->current_blockheight, - &scid, source_peer); - if (err) - return err; - else if (scid) { - /* We give them some grace period, in case we don't know about - * block yet. */ - if (daemon->current_blockheight == 0 - || !is_scid_depth_announceable(scid, - daemon->current_blockheight)) { - tal_arr_expand(&daemon->deferred_txouts, *scid); - } else { - daemon_conn_send(daemon->master, - take(towire_gossipd_get_txout(NULL, - scid))); - } - } - return NULL; -} - -static u8 *handle_channel_update_msg(struct peer *peer, const u8 *msg) -{ - struct short_channel_id unknown_scid; - /* Hand the channel_update to the routing code */ - u8 *err; - - unknown_scid.u64 = 0; - err = handle_channel_update(peer->daemon->rstate, msg, &peer->id, - &unknown_scid, false); - if (err) - return err; - - /* If it's an unknown channel, ask someone about it */ - if (unknown_scid.u64 != 0) - query_unknown_channel(peer->daemon, peer, &unknown_scid); - - return NULL; -} - -static u8 *handle_node_announce(struct peer *peer, const u8 *msg) -{ - bool was_unknown = false; - u8 *err; - - err = handle_node_announcement(peer->daemon->rstate, msg, &peer->id, - &was_unknown); - if (was_unknown) - query_unknown_node(peer->daemon->seeker, peer); - return err; -} - -/* Statistically, how many peers to we tell about each channel? */ -#define GOSSIP_SPAM_REDUNDANCY 5 - -/* BOLT #7: - * - if the `gossip_queries` feature is negotiated: - * - MUST NOT relay any gossip messages it did not generate itself, - * unless explicitly requested. - */ -/* i.e. the strong implication is that we spam our own gossip aggressively! - * "Look at me!" "Look at me!!!!". + * We send back a warning if they send us something bogus. */ -static void dump_our_gossip(struct daemon *daemon, struct peer *peer) -{ - struct node *me; - struct chan_map_iter it; - const struct chan *chan, **chans = tal_arr(tmpctx, const struct chan *, 0); - size_t num_to_send; - - /* Find ourselves; if no channels, nothing to send */ - me = get_node(daemon->rstate, &daemon->id); - if (!me) - return; - - for (chan = first_chan(me, &it); chan; chan = next_chan(me, &it)) { - tal_arr_expand(&chans, chan); - } - - /* Just in case we have many peers and not all are connecting or - * some other corner case, send everything to first few. */ - if (peer_node_id_map_count(daemon->peers) <= GOSSIP_SPAM_REDUNDANCY) - num_to_send = tal_count(chans); - else { - if (tal_count(chans) < GOSSIP_SPAM_REDUNDANCY) - num_to_send = tal_count(chans); - else { - /* Pick victims at random */ - tal_arr_randomize(chans, const struct chan *); - num_to_send = GOSSIP_SPAM_REDUNDANCY; - } - } - - for (size_t i = 0; i < num_to_send; i++) { - chan = chans[i]; - - /* Send channel_announce */ - queue_peer_from_store(peer, &chan->bcast); - - /* Send both channel_updates (if they exist): both help people - * use our channel, so we care! */ - for (int dir = 0; dir < 2; dir++) { - if (is_halfchan_defined(&chan->half[dir])) - queue_peer_from_store(peer, &chan->half[dir].bcast); - } - } - - /* If we have one, we should send our own node_announcement */ - if (me->bcast.index) - queue_peer_from_store(peer, &me->bcast); -} /*~ This is where connectd tells us about a new peer we might want to * gossip with. */ @@ -348,7 +139,7 @@ static void connectd_new_peer(struct daemon *daemon, const u8 *msg) tal_add_destructor(peer, destroy_peer); /* Send everything we know about our own channels */ - dump_our_gossip(daemon, peer); + gossmap_manage_new_peer(daemon->gm, &peer->id); /* This sends the initial timestamp filter. */ seeker_setup_peer_gossip(daemon->seeker, peer); @@ -378,19 +169,14 @@ static struct io_plan *handle_get_address(struct io_conn *conn, const u8 *msg) { struct node_id id; - u8 rgb_color[3]; - u8 alias[32]; - u8 *features; struct wireaddr *addrs; - struct lease_rates *rates; if (!fromwire_gossipd_get_addrs(msg, &id)) master_badmsg(WIRE_GOSSIPD_GET_ADDRS, msg); - if (!get_node_announcement_by_id(tmpctx, daemon, &id, - rgb_color, alias, &features, &addrs, - &rates)) - addrs = NULL; + addrs = gossmap_manage_get_node_addresses(tmpctx, + daemon->gm, + &id); daemon_conn_send(daemon->master, take(towire_gossipd_get_addrs_reply(NULL, addrs))); @@ -399,36 +185,44 @@ static struct io_plan *handle_get_address(struct io_conn *conn, static void handle_recv_gossip(struct daemon *daemon, const u8 *outermsg) { - struct node_id id; + struct node_id source; u8 *msg; const u8 *err; + const char *errmsg; struct peer *peer; - if (!fromwire_gossipd_recv_gossip(outermsg, outermsg, &id, &msg)) { + if (!fromwire_gossipd_recv_gossip(outermsg, outermsg, &source, &msg)) { status_failed(STATUS_FAIL_INTERNAL_ERROR, "Bad gossipd_recv_gossip msg from connectd: %s", tal_hex(tmpctx, outermsg)); } - peer = find_peer(daemon, &id); + peer = find_peer(daemon, &source); if (!peer) { status_broken("connectd sent gossip msg %s from unknown peer %s", peer_wire_name(fromwire_peektype(msg)), - type_to_string(tmpctx, struct node_id, &id)); + type_to_string(tmpctx, struct node_id, &source)); return; } + status_peer_debug(&source, "handle_recv_gossip: %s", peer_wire_name(fromwire_peektype(msg))); /* These are messages relayed from peer */ switch ((enum peer_wire)fromwire_peektype(msg)) { case WIRE_CHANNEL_ANNOUNCEMENT: - err = handle_channel_announcement_msg(peer->daemon, &id, msg); - goto handled_msg; + errmsg = gossmap_manage_channel_announcement(tmpctx, + daemon->gm, + msg, &source, NULL); + goto handled_msg_errmsg; case WIRE_CHANNEL_UPDATE: - err = handle_channel_update_msg(peer, msg); - goto handled_msg; + errmsg = gossmap_manage_channel_update(tmpctx, + daemon->gm, + msg, &source); + goto handled_msg_errmsg; case WIRE_NODE_ANNOUNCEMENT: - err = handle_node_announce(peer, msg); - goto handled_msg; + errmsg = gossmap_manage_node_announcement(tmpctx, + daemon->gm, + msg, &source); + goto handled_msg_errmsg; case WIRE_QUERY_CHANNEL_RANGE: err = handle_query_channel_range(peer, msg); goto handled_msg; @@ -492,9 +286,19 @@ static void handle_recv_gossip(struct daemon *daemon, const u8 *outermsg) peer_wire_name(fromwire_peektype(msg)), type_to_string(tmpctx, struct node_id, &peer->id)); +handled_msg_errmsg: + if (errmsg) + err = towire_warningfmt(NULL, NULL, "%s", errmsg); + else + err = NULL; + handled_msg: - if (err) - queue_peer_msg(peer, take(err)); + if (err) { + queue_peer_msg(daemon, &source, take(err)); + } else { + /* Some peer gave us gossip, so we're not at zero. */ + peer->daemon->gossip_store_populated = true; + } } /*~ connectd's input handler is very simple. */ @@ -529,67 +333,25 @@ static struct io_plan *connectd_req(struct io_conn *conn, return daemon_conn_read_next(conn, daemon->connectd); } -/* BOLT #7: - * - * A node: - * - if the `timestamp` of the latest `channel_update` in - * either direction is older than two weeks (1209600 seconds): - * - MAY prune the channel. - * - MAY ignore the channel. - */ -static void gossip_refresh_network(struct daemon *daemon) +void tell_lightningd_peer_update(struct daemon *daemon, + const struct node_id *source_peer, + struct short_channel_id scid, + u32 fee_base_msat, + u32 fee_ppm, + u16 cltv_delta, + struct amount_msat htlc_minimum, + struct amount_msat htlc_maximum) { - /* Schedule next run now */ - notleak(new_reltimer(&daemon->timers, daemon, - time_from_sec(GOSSIP_PRUNE_INTERVAL(daemon->rstate->dev_fast_gossip_prune)/4), - gossip_refresh_network, daemon)); - - /* Prune: I hope lightningd is keeping up with our own channel - * refreshes! */ - route_prune(daemon->rstate); -} - -static void tell_master_local_cupdates(struct daemon *daemon) -{ - struct chan_map_iter i; - struct chan *c; - struct node *me; - - me = get_node(daemon->rstate, &daemon->id); - if (!me) - return; - - for (c = first_chan(me, &i); c; c = next_chan(me, &i)) { - struct half_chan *hc; - int direction; - const u8 *cupdate; - - if (!local_direction(daemon->rstate, c, &direction)) - continue; - - hc = &c->half[direction]; - if (!is_halfchan_defined(hc)) - continue; - - cupdate = gossip_store_get(tmpctx, - daemon->rstate->gs, - hc->bcast.index); - daemon_conn_send(daemon->master, - take(towire_gossipd_init_cupdate(NULL, - &c->scid, - cupdate))); - } - - /* Tell lightningd about our current node_announcement, if any */ - if (me->bcast.index) { - const u8 *nannounce; - nannounce = gossip_store_get(tmpctx, - daemon->rstate->gs, - me->bcast.index); - daemon_conn_send(daemon->master, - take(towire_gossipd_init_nannounce(NULL, - nannounce))); - } + struct peer_update remote_update; + u8* msg; + remote_update.scid = scid; + remote_update.fee_base = fee_base_msat; + remote_update.fee_ppm = fee_ppm; + remote_update.cltv_delta = cltv_delta; + remote_update.htlc_minimum_msat = htlc_minimum; + remote_update.htlc_maximum_msat = htlc_maximum; + msg = towire_gossipd_remote_channel_update(NULL, source_peer, &remote_update); + daemon_conn_send(daemon->master, take(msg)); } struct peer *first_random_peer(struct daemon *daemon, @@ -623,42 +385,60 @@ static void master_or_connectd_gone(struct daemon_conn *dc UNUSED) exit(2); } +struct timeabs gossip_time_now(const struct daemon *daemon) +{ + if (daemon->dev_gossip_time) + return *daemon->dev_gossip_time; + + return time_now(); +} + +/* We don't check this when loading from the gossip_store: that would break + * our canned tests, and usually old gossip is better than no gossip */ +bool timestamp_reasonable(const struct daemon *daemon, u32 timestamp) +{ + u64 now = gossip_time_now(daemon).ts.tv_sec; + + /* More than one day ahead? */ + if (timestamp > now + 24*60*60) + return false; + /* More than 2 weeks behind? */ + if (timestamp < now - GOSSIP_PRUNE_INTERVAL(daemon->dev_fast_gossip_prune)) + return false; + return true; +} + /*~ Parse init message from lightningd: starts the daemon properly. */ static void gossip_init(struct daemon *daemon, const u8 *msg) { u32 *dev_gossip_time; - bool dev_fast_gossip, dev_fast_gossip_prune; - u32 timestamp; + struct chan_dying *dying; if (!fromwire_gossipd_init(daemon, msg, &chainparams, &daemon->our_features, &daemon->id, &dev_gossip_time, - &dev_fast_gossip, - &dev_fast_gossip_prune)) { + &daemon->dev_fast_gossip, + &daemon->dev_fast_gossip_prune)) { master_badmsg(WIRE_GOSSIPD_INIT, msg); } - daemon->rstate = new_routing_state(daemon, - daemon, - take(dev_gossip_time), - dev_fast_gossip, - dev_fast_gossip_prune); - - /* Load stored gossip messages, get last modified time of file */ - timestamp = gossip_store_load(daemon->rstate, daemon->rstate->gs); + if (dev_gossip_time) { + assert(daemon->developer); + daemon->dev_gossip_time = tal(daemon, struct timeabs); + daemon->dev_gossip_time->ts.tv_sec = *dev_gossip_time; + daemon->dev_gossip_time->ts.tv_nsec = 0; + tal_free(dev_gossip_time); + } - /* If last_timestamp was > modified time of file, reduce it. - * Usually it's capped to "now", but in the reload case it needs to - * be the gossip_store mtime. */ - if (daemon->rstate->last_timestamp > timestamp) - daemon->rstate->last_timestamp = timestamp; + daemon->gs = gossip_store_new(daemon, + daemon, + &daemon->gossip_store_populated, + &dying); - /* Start the twice- weekly refresh timer. */ - notleak(new_reltimer(&daemon->timers, daemon, - time_from_sec(GOSSIP_PRUNE_INTERVAL(daemon->rstate->dev_fast_gossip_prune) / 4), - gossip_refresh_network, daemon)); + /* Gossmap manager starts up */ + daemon->gm = gossmap_manage_new(daemon, daemon, take(dying)); /* Fire up the seeker! */ daemon->seeker = new_seeker(daemon); @@ -671,7 +451,7 @@ static void gossip_init(struct daemon *daemon, const u8 *msg) /* Tell it about all our local (public) channel_update messages, * and node_announcement, so it doesn't unnecessarily regenerate them. */ - tell_master_local_cupdates(daemon); + gossmap_manage_tell_lightningd_locals(daemon, daemon->gm); /* OK, we are ready. */ daemon_conn_send(daemon->master, @@ -700,7 +480,7 @@ static void new_blockheight(struct daemon *daemon, const u8 *msg) i--; } - routing_expire_channels(daemon->rstate, daemon->current_blockheight); + gossmap_manage_new_block(daemon->gm, daemon->current_blockheight); daemon_conn_send(daemon->master, take(towire_gossipd_new_blockheight_reply(NULL))); @@ -724,45 +504,16 @@ static void dev_gossip_memleak(struct daemon *daemon, const u8 *msg) found_leak))); } -static void dev_compact_store(struct daemon *daemon, const u8 *msg) -{ - bool done = gossip_store_compact(daemon->rstate->gs); - - daemon_conn_send(daemon->master, - take(towire_gossipd_dev_compact_store_reply(NULL, - done))); -} - static void dev_gossip_set_time(struct daemon *daemon, const u8 *msg) { u32 time; if (!fromwire_gossipd_dev_set_time(msg, &time)) master_badmsg(WIRE_GOSSIPD_DEV_SET_TIME, msg); - if (!daemon->rstate->dev_gossip_time) - daemon->rstate->dev_gossip_time = tal(daemon->rstate, struct timeabs); - daemon->rstate->dev_gossip_time->ts.tv_sec = time; - daemon->rstate->dev_gossip_time->ts.tv_nsec = 0; -} - -/*~ We queue incoming channel_announcement pending confirmation from lightningd - * that it really is an unspent output. Here's its reply. */ -static void handle_txout_reply(struct daemon *daemon, const u8 *msg) -{ - struct short_channel_id scid; - u8 *outscript; - struct amount_sat sat; - bool good; - - if (!fromwire_gossipd_get_txout_reply(msg, msg, &scid, &sat, &outscript)) - master_badmsg(WIRE_GOSSIPD_GET_TXOUT_REPLY, msg); - - /* Outscript is NULL if it's not an unspent output */ - good = handle_pending_cannouncement(daemon, daemon->rstate, - &scid, sat, outscript); - - /* If we looking specifically for this, we no longer are. */ - remove_unknown_scid(daemon->seeker, &scid, good); + if (!daemon->dev_gossip_time) + daemon->dev_gossip_time = tal(daemon, struct timeabs); + daemon->dev_gossip_time->ts.tv_sec = time; + daemon->dev_gossip_time->ts.tv_nsec = 0; } /*~ lightningd tells us when about a gossip message directly, when told to by @@ -771,42 +522,38 @@ static void handle_txout_reply(struct daemon *daemon, const u8 *msg) static void inject_gossip(struct daemon *daemon, const u8 *msg) { u8 *goss; - const u8 *errmsg; const char *err; + struct amount_sat *known_amount; - if (!fromwire_gossipd_addgossip(msg, msg, &goss)) + if (!fromwire_gossipd_addgossip(msg, msg, &goss, &known_amount)) master_badmsg(WIRE_GOSSIPD_ADDGOSSIP, msg); + status_debug("inject_gossip: %s", peer_wire_name(fromwire_peektype(goss))); switch (fromwire_peektype(goss)) { case WIRE_CHANNEL_ANNOUNCEMENT: - errmsg = handle_channel_announcement_msg(daemon, NULL, goss); + err = gossmap_manage_channel_announcement(tmpctx, + daemon->gm, + take(goss), NULL, + known_amount); break; case WIRE_NODE_ANNOUNCEMENT: - errmsg = handle_node_announcement(daemon->rstate, goss, - NULL, NULL); + err = gossmap_manage_node_announcement(tmpctx, + daemon->gm, + take(goss), NULL); break; case WIRE_CHANNEL_UPDATE: - errmsg = handle_channel_update(daemon->rstate, goss, - NULL, NULL, true); + err = gossmap_manage_channel_update(tmpctx, + daemon->gm, + take(goss), NULL); break; default: err = tal_fmt(tmpctx, "unknown gossip type %i", fromwire_peektype(goss)); - goto err_extracted; } - /* The APIs above are designed to send error messages back to peers: - * we extract the raw string instead. */ - if (errmsg) { - err = sanitize_error(tmpctx, errmsg, NULL); - tal_free(errmsg); - } else - /* Send empty string if no error. */ - err = ""; - -err_extracted: + /* FIXME: Make this an optional string in gossipd_addgossip_reply */ daemon_conn_send(daemon->master, - take(towire_gossipd_addgossip_reply(NULL, err))); + take(towire_gossipd_addgossip_reply(NULL, err ? err : ""))); } /*~ This is where lightningd tells us that a channel's funding transaction has @@ -819,16 +566,8 @@ static void handle_outpoints_spent(struct daemon *daemon, const u8 *msg) if (!fromwire_gossipd_outpoints_spent(msg, msg, &blockheight, &scids)) master_badmsg(WIRE_GOSSIPD_OUTPOINTS_SPENT, msg); - for (size_t i = 0; i < tal_count(scids); i++) { - struct chan *chan = get_channel(daemon->rstate, &scids[i]); - - if (!chan) - continue; - - /* We have a current_blockheight, but it's not necessarily - * updated first. */ - routing_channel_spent(daemon->rstate, blockheight, chan); - } + for (size_t i = 0; i < tal_count(scids); i++) + gossmap_manage_channel_spent(daemon->gm, blockheight, scids[i]); } /*~ This routine handles all the commands from lightningd. */ @@ -844,7 +583,7 @@ static struct io_plan *recv_req(struct io_conn *conn, goto done; case WIRE_GOSSIPD_GET_TXOUT_REPLY: - handle_txout_reply(daemon, msg); + gossmap_manage_handle_get_txout_reply(daemon->gm, msg); goto done; case WIRE_GOSSIPD_OUTPOINTS_SPENT: @@ -874,12 +613,6 @@ static struct io_plan *recv_req(struct io_conn *conn, goto done; } /* fall thru */ - case WIRE_GOSSIPD_DEV_COMPACT_STORE: - if (daemon->developer) { - dev_compact_store(daemon, msg); - goto done; - } - /* fall thru */ case WIRE_GOSSIPD_DEV_SET_TIME: if (daemon->developer) { dev_gossip_set_time(daemon, msg); @@ -893,7 +626,6 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_INIT_REPLY: case WIRE_GOSSIPD_GET_TXOUT: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: - case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY: case WIRE_GOSSIPD_ADDGOSSIP_REPLY: case WIRE_GOSSIPD_NEW_BLOCKHEIGHT_REPLY: case WIRE_GOSSIPD_GET_ADDRS_REPLY: @@ -920,6 +652,7 @@ int main(int argc, char *argv[]) daemon = tal(NULL, struct daemon); daemon->developer = developer; + daemon->dev_gossip_time = NULL; daemon->peers = tal(daemon, struct peer_node_id_map); peer_node_id_map_init(daemon->peers); daemon->deferred_txouts = tal_arr(daemon, struct short_channel_id, 0); diff --git a/gossipd/gossipd.h b/gossipd/gossipd.h index 6d4b6ddbf902..2c1216bea7cb 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -50,8 +50,8 @@ struct daemon { /* Connection to connect daemon. */ struct daemon_conn *connectd; - /* Routing information */ - struct routing_state *rstate; + /* Manager of writing to the gossip_store */ + struct gossmap_manage *gm; /* Timers: we batch gossip, and also refresh announcements */ struct timers timers; @@ -64,6 +64,21 @@ struct daemon { /* Features lightningd told us to set. */ struct feature_set *our_features; + + /* Gossip store */ + struct gossip_store *gs; + + /* Was there anything in the gossip store at startup? */ + bool gossip_store_populated; + + /* Override local time for gossip messages */ + struct timeabs *dev_gossip_time; + + /* Speed up gossip. */ + bool dev_fast_gossip; + + /* Speed up pruning. */ + bool dev_fast_gossip_prune; }; struct range_query_reply { @@ -129,11 +144,30 @@ struct peer *next_random_peer(struct daemon *daemon, /* Queue a gossip message for the peer: the subdaemon on the other end simply * forwards it to the peer. */ -void queue_peer_msg(struct peer *peer, const u8 *msg TAKES); - -/* Queue a gossip_store message for the peer: the subdaemon on the - * other end simply forwards it to the peer. */ -void queue_peer_from_store(struct peer *peer, - const struct broadcastable *bcast); +void queue_peer_msg(struct daemon *daemon, + const struct node_id *peer, + const u8 *msg TAKES); + +/* We have an update for one of our channels (or unknown). */ +void tell_lightningd_peer_update(struct daemon *daemon, + const struct node_id *source_peer, + struct short_channel_id scid, + u32 fee_base_msat, + u32 fee_ppm, + u16 cltv_delta, + struct amount_msat htlc_minimum, + struct amount_msat htlc_maximum); + +/** + * Get the local time. + * + * This gets overridden in dev mode so we can use canned (stale) gossip. + */ +struct timeabs gossip_time_now(const struct daemon *daemon); + +/** + * Is this gossip timestamp reasonable? + */ +bool timestamp_reasonable(const struct daemon *daemon, u32 timestamp); #endif /* LIGHTNING_GOSSIPD_GOSSIPD_H */ diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 63dabd05ea5b..9ffabb791446 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -57,13 +57,6 @@ msgtype,gossipd_dev_memleak,3033 msgtype,gossipd_dev_memleak_reply,3133 msgdata,gossipd_dev_memleak_reply,leak,bool, -# master -> gossipd: please rewrite the gossip_store -msgtype,gossipd_dev_compact_store,3034 - -# gossipd -> master: ok -msgtype,gossipd_dev_compact_store_reply,3134 -msgdata,gossipd_dev_compact_store_reply,success,bool, - # master -> gossipd: blockheight increased. msgtype,gossipd_new_blockheight,3026 msgdata,gossipd_new_blockheight,blockheight,u32, @@ -75,6 +68,7 @@ msgtype,gossipd_new_blockheight_reply,3126 msgtype,gossipd_addgossip,3044 msgdata,gossipd_addgossip,len,u16, msgdata,gossipd_addgossip,msg,u8,len +msgdata,gossipd_addgossip,known_channel,?amount_sat, # Empty string means no problem. msgtype,gossipd_addgossip_reply,3144 diff --git a/gossipd/gossmap_manage.c b/gossipd/gossmap_manage.c new file mode 100644 index 000000000000..e540eb0092a1 --- /dev/null +++ b/gossipd/gossmap_manage.c @@ -0,0 +1,1316 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct pending_cannounce { + const u8 *scriptpubkey; + const u8 *channel_announcement; + const struct node_id *source_peer; +}; + +struct pending_cupdate { + struct short_channel_id scid; + secp256k1_ecdsa_signature signature; + u8 message_flags; + u8 channel_flags; + u16 cltv_expiry_delta; + struct amount_msat htlc_minimum_msat, htlc_maximum_msat; + u32 fee_base_msat, fee_proportional_millionths; + u32 timestamp; + const u8 *update; + const struct node_id *source_peer; +}; + +struct pending_nannounce { + struct node_id node_id; + u32 timestamp; + const u8 *nannounce; + const struct node_id *source_peer; +}; + +struct cannounce_map { + UINTMAP(struct pending_cannounce *) map; + size_t count; + + /* Name, for flood reporting */ + const char *name; + bool flood_reported; +}; + +struct gossmap_manage { + struct daemon *daemon; + + /* For us to write to gossip_store */ + int fd; + + /* gossip map itself (access via gossmap_manage_get_gossmap, so it's fresh!) */ + struct gossmap *raw_gossmap; + + /* Announcements we're checking, indexed by scid */ + struct cannounce_map pending_ann_map; + + /* Updates we've deferred for above */ + struct pending_cupdate **pending_cupdates; + + /* Announcements which are too early to check. */ + struct cannounce_map early_ann_map; + struct pending_cupdate **early_cupdates; + + /* Node announcements (waiting for a pending_cannounce maybe) */ + struct pending_nannounce **pending_nannounces; + + /* Lookups we've failed recently */ + struct txout_failures *txf; + + /* Blockheights of scids to remove */ + struct chan_dying *dying_channels; + + /* Occasional check for dead channels */ + struct oneshot *prune_timer; +}; + +/* Timer recursion */ +static void start_prune_timer(struct gossmap_manage *gm); + +static void enqueue_cupdate(struct pending_cupdate ***queue, + struct short_channel_id scid, + const secp256k1_ecdsa_signature *signature, + u8 message_flags, + u8 channel_flags, + u16 cltv_expiry_delta, + struct amount_msat htlc_minimum_msat, + struct amount_msat htlc_maximum_msat, + u32 fee_base_msat, + u32 fee_proportional_millionths, + u32 timestamp, + const u8 *update TAKES, + const struct node_id *source_peer TAKES) +{ + struct pending_cupdate *pcu = tal(*queue, struct pending_cupdate); + + pcu->scid = scid; + pcu->signature = *signature; + pcu->message_flags = message_flags; + pcu->channel_flags = channel_flags; + pcu->cltv_expiry_delta = cltv_expiry_delta; + pcu->htlc_minimum_msat = htlc_minimum_msat; + pcu->htlc_maximum_msat = htlc_maximum_msat; + pcu->fee_base_msat = fee_base_msat; + pcu->fee_proportional_millionths = fee_proportional_millionths; + pcu->timestamp = timestamp; + pcu->update = tal_dup_talarr(pcu, u8, update); + pcu->source_peer = tal_dup_or_null(pcu, struct node_id, source_peer); + + tal_arr_expand(queue, pcu); +} + +static void enqueue_nannounce(struct pending_nannounce ***queue, + const struct node_id *node_id, + u32 timestamp, + const u8 *nannounce TAKES, + const struct node_id *source_peer TAKES) +{ + struct pending_nannounce *pna = tal(*queue, struct pending_nannounce); + + pna->node_id = *node_id; + pna->timestamp = timestamp; + pna->nannounce = tal_dup_talarr(pna, u8, nannounce); + pna->source_peer = tal_dup_or_null(pna, struct node_id, source_peer); + + tal_arr_expand(queue, pna); +} + +/* Helpers to keep counters in sync with maps! */ +static void map_init(struct cannounce_map *map, const char *name) +{ + uintmap_init(&map->map); + map->count = 0; + map->name = name; + map->flood_reported = false; +} + +static bool map_add(struct cannounce_map *map, + struct short_channel_id scid, + struct pending_cannounce *pca) +{ + /* More than 10000 pending things? Stop! */ + if (map->count > 10000) { + if (!map->flood_reported) { + status_unusual("%s being flooded by %s: dropping some", + map->name, + pca->source_peer + ? node_id_to_hexstr(tmpctx, pca->source_peer) + : "unknown"); + map->flood_reported = true; + } + return false; + } + + if (uintmap_add(&map->map, scid.u64, pca)) { + map->count++; + return true; + } + return false; +} + +static struct pending_cannounce *map_del(struct cannounce_map *map, + struct short_channel_id scid) +{ + struct pending_cannounce *pca = uintmap_del(&map->map, scid.u64); + if (pca) { + assert(map->count); + map->count--; + if (map->flood_reported && uintmap_empty(&map->map)) { + status_unusual("%s flood has subsided", map->name); + map->flood_reported = false; + } + } + return pca; +} + +static bool map_empty(const struct cannounce_map *map) +{ + if (uintmap_empty(&map->map)) { + assert(map->count == 0); + return true; + } + assert(map->count != 0); + return false; +} + +static struct pending_cannounce *map_get(struct cannounce_map *map, + struct short_channel_id scid) +{ + return uintmap_get(&map->map, scid.u64); +} + +/* Does any channel_announcement preceed this offset in the gossip_store? */ +static bool any_cannounce_preceeds_offset(struct gossmap *gossmap, + const struct gossmap_node *node, + const struct gossmap_chan *exclude_chan, + u64 offset) +{ + for (size_t i = 0; i < node->num_chans; i++) { + struct gossmap_chan *chan = gossmap_nth_chan(gossmap, node, i, NULL); + + if (chan == exclude_chan) + continue; + if (chan->cann_off < offset) + return true; + } + return false; +} + +/* To actually remove a channel: + * - Suppress future lookups in case we receive another channel_update. + * - Put deleted tombstone in gossip_store. + * - Mark records deleted in gossip_store. + * - See if node_announcement(s) need to be removed, or moved. + */ +static void remove_channel(struct gossmap_manage *gm, + struct gossmap *gossmap, + struct gossmap_chan *chan, + struct short_channel_id scid) +{ + /* Suppress any now-obsolete updates/announcements */ + txout_failures_add(gm->txf, scid); + + /* Cover race where we were looking up this UTXO as it was spent. */ + tal_free(map_del(&gm->pending_ann_map, scid)); + tal_free(map_del(&gm->early_ann_map, scid)); + + /* Put in tombstone marker. */ + gossip_store_add(gm->daemon->gs, + towire_gossip_store_delete_chan(tmpctx, &scid), + 0); + + /* Delete from store */ + gossip_store_del(gm->daemon->gs, chan->cann_off, WIRE_CHANNEL_ANNOUNCEMENT); + for (int dir = 0; dir < 2; dir++) { + if (gossmap_chan_set(chan, dir)) + gossip_store_del(gm->daemon->gs, chan->cupdate_off[dir], WIRE_CHANNEL_UPDATE); + } + + /* Check for node_announcements which should no longer be there */ + for (int dir = 0; dir < 2; dir++) { + struct gossmap_node *node; + const u8 *nannounce; + u32 timestamp; + + node = gossmap_nth_node(gossmap, chan, dir); + + /* If there was a node announcement, we might need to fix things up. */ + if (!gossmap_node_announced(node)) + continue; + + /* Last channel? Delete node announce */ + if (node->num_chans == 1) { + gossip_store_del(gm->daemon->gs, node->nann_off, WIRE_NODE_ANNOUNCEMENT); + continue; + } + + /* Maybe this was the last channel_announcement which preceeded node_announcement? */ + if (chan->cann_off > node->nann_off) + continue; + + if (any_cannounce_preceeds_offset(gossmap, node, chan, node->nann_off)) + continue; + + /* To maintain order, delete and re-add node_announcement */ + nannounce = gossmap_node_get_announce(tmpctx, gossmap, node); + timestamp = gossip_store_get_timestamp(gm->daemon->gs, node->nann_off); + gossip_store_del(gm->daemon->gs, node->nann_off, WIRE_NODE_ANNOUNCEMENT); + gossip_store_add(gm->daemon->gs, nannounce, timestamp); + } +} + +static u32 get_timestamp(struct gossmap *gossmap, + const struct gossmap_chan *chan, + int dir) +{ + u32 timestamp; + + /* 0 is sufficient for our needs */ + if (!gossmap_chan_set(chan, dir)) + return 0; + + gossmap_chan_get_update_details(gossmap, chan, dir, + ×tamp, + NULL, NULL, NULL, NULL, NULL, NULL); + return timestamp; +} + +/* Every half a week we look for dead channels (faster in dev) */ +static void prune_network(struct gossmap_manage *gm) +{ + u64 now = gossip_time_now(gm->daemon).ts.tv_sec; + /* Anything below this highwater mark ought to be pruned */ + const s64 highwater = now - GOSSIP_PRUNE_INTERVAL(gm->daemon->dev_fast_gossip_prune); + const struct gossmap_node *me; + struct gossmap *gossmap; + + /* We reload this every time we delete a channel: that way we can tell if it's + * time to remove a node! */ + gossmap = gossmap_manage_get_gossmap(gm); + me = gossmap_find_node(gossmap, &gm->daemon->id); + + /* Now iterate through all channels and see if it is still alive */ + for (size_t i = 0; i < gossmap_max_chan_idx(gossmap); i++) { + struct gossmap_chan *chan = gossmap_chan_byidx(gossmap, i); + u32 timestamp[2]; + struct short_channel_id scid; + + if (!chan) + continue; + + /* BOLT #7: + * - if the `timestamp` of the latest `channel_update` in + * either direction is older than two weeks (1209600 seconds): + * - MAY prune the channel. + */ + /* This is a fancy way of saying "both ends must refresh!" */ + timestamp[0] = get_timestamp(gossmap, chan, 0); + timestamp[1] = get_timestamp(gossmap, chan, 1); + + if (timestamp[0] >= highwater && timestamp[1] >= highwater) + continue; + + scid = gossmap_chan_scid(gossmap, chan); + + /* Is it one of mine? */ + if (gossmap_nth_node(gossmap, chan, 0) == me + || gossmap_nth_node(gossmap, chan, 1) == me) { + int local = (gossmap_nth_node(gossmap, chan, 1) == me); + status_unusual("Pruning local channel %s from gossip_store: local channel_update time %u, remote %u", + type_to_string(tmpctx, struct short_channel_id, + &scid), + timestamp[local], timestamp[!local]); + } + + status_debug("Pruning channel %s from network view (ages %u and %u)", + type_to_string(tmpctx, struct short_channel_id, + &scid), + timestamp[0], timestamp[1]); + + remove_channel(gm, gossmap, chan, scid); + + gossmap = gossmap_manage_get_gossmap(gm); + me = gossmap_find_node(gossmap, &gm->daemon->id); + } + + /* Note: some nodes may have been left with no channels! Gossmap will + * remove them on next refresh. */ + start_prune_timer(gm); +} + +static void start_prune_timer(struct gossmap_manage *gm) +{ + /* Schedule next run now */ + gm->prune_timer = new_reltimer(&gm->daemon->timers, gm, + time_from_sec(GOSSIP_PRUNE_INTERVAL(gm->daemon->dev_fast_gossip_prune)/4), + prune_network, gm); +} + +static void reprocess_queued_msgs(struct gossmap_manage *gm); + +static void report_bad_update(struct gossmap *map, + const struct short_channel_id_dir *scidd, + u16 cltv_expiry_delta, + u32 fee_base_msat, + u32 fee_proportional_millionths, + struct gossmap_manage *gm) +{ + status_debug("Update for %s has silly values, disabling (cltv=%u, fee=%u+%u)", + type_to_string(tmpctx, struct short_channel_id_dir, scidd), + cltv_expiry_delta, fee_base_msat, fee_proportional_millionths); +} + +struct gossmap_manage *gossmap_manage_new(const tal_t *ctx, + struct daemon *daemon, + struct chan_dying *dying_channels TAKES) +{ + struct gossmap_manage *gm = tal(ctx, struct gossmap_manage); + + gm->fd = gossip_store_get_fd(daemon->gs); + gm->raw_gossmap = gossmap_load_fd(gm, gm->fd, report_bad_update, NULL, gm); + assert(gm->raw_gossmap); + gm->daemon = daemon; + + map_init(&gm->pending_ann_map, "pending announcements"); + gm->pending_cupdates = tal_arr(gm, struct pending_cupdate *, 0); + map_init(&gm->early_ann_map, "too-early announcements"); + gm->early_cupdates = tal_arr(gm, struct pending_cupdate *, 0); + gm->pending_nannounces = tal_arr(gm, struct pending_nannounce *, 0); + gm->txf = txout_failures_new(gm, daemon); + gm->dying_channels = tal_dup_talarr(gm, struct chan_dying, dying_channels); + + start_prune_timer(gm); + return gm; +} + +/* Catch CI giving out-of-order gossip: definitely happens IRL though */ +static void bad_gossip(const struct node_id *source_peer, const char *str) +{ + status_peer_debug(source_peer, "Bad gossip order: %s", str); +} + +/* Send peer a warning message, if non-NULL. */ +static void peer_warning(struct gossmap_manage *gm, + const struct node_id *source_peer, + const char *fmt, ...) +{ + va_list ap; + char *formatted; + + va_start(ap, fmt); + formatted = tal_vfmt(tmpctx, fmt, ap); + va_end(ap); + + bad_gossip(source_peer, formatted); + if (!source_peer) + return; + + queue_peer_msg(gm->daemon, source_peer, + take(towire_warningfmt(NULL, NULL, "%s", formatted))); +} + +const char *gossmap_manage_channel_announcement(const tal_t *ctx, + struct gossmap_manage *gm, + const u8 *announce TAKES, + const struct node_id *source_peer TAKES, + const struct amount_sat *known_amount) +{ + secp256k1_ecdsa_signature node_signature_1, node_signature_2; + secp256k1_ecdsa_signature bitcoin_signature_1, bitcoin_signature_2; + u8 *features; + struct bitcoin_blkid chain_hash; + struct short_channel_id scid; + struct node_id node_id_1; + struct node_id node_id_2; + struct pubkey bitcoin_key_1; + struct pubkey bitcoin_key_2; + struct pending_cannounce *pca; + const char *warn; + u32 blockheight = gm->daemon->current_blockheight; + struct gossmap *gossmap = gossmap_manage_get_gossmap(gm); + + /* Make sure we own msg, even if we don't save it. */ + if (taken(announce)) + tal_steal(tmpctx, announce); + + if (!fromwire_channel_announcement(tmpctx, announce, &node_signature_1, &node_signature_2, + &bitcoin_signature_1, &bitcoin_signature_2, &features, &chain_hash, + &scid, &node_id_1, &node_id_2, &bitcoin_key_1, &bitcoin_key_2)) { + return tal_fmt(ctx, "Malformed channel_announcement %s", + tal_hex(tmpctx, announce)); + } + + /* If a prior txout lookup failed there is little point it trying + * again. Just drop the announcement and walk away whistling. + * + * Happens quite a lot in CI on just-closed channels. + */ + if (in_txout_failures(gm->txf, scid)) { + return NULL; + } + + warn = sigcheck_channel_announcement(ctx, &node_id_1, &node_id_2, + &bitcoin_key_1, &bitcoin_key_2, + &node_signature_1, &node_signature_2, + &bitcoin_signature_1, &bitcoin_signature_2, + announce); + if (warn) + return warn; + + /* Already known? */ + if (gossmap_find_chan(gossmap, &scid)) + return NULL; + + pca = tal(gm, struct pending_cannounce); + pca->scriptpubkey = scriptpubkey_p2wsh(pca, + bitcoin_redeem_2of2(tmpctx, + &bitcoin_key_1, + &bitcoin_key_2)); + pca->channel_announcement = tal_dup_talarr(pca, u8, announce); + pca->source_peer = tal_dup_or_null(pca, struct node_id, source_peer); + + /* Are we supposed to add immediately without checking with lightningd? + * Unless we already got it from a peer and we're processing now! + */ + if (known_amount + && !map_get(&gm->pending_ann_map, scid) + && !map_get(&gm->early_ann_map, scid)) { + /* Set with timestamp 0 (we will update once we have a channel_update) */ + gossip_store_add(gm->daemon->gs, announce, 0); + gossip_store_add(gm->daemon->gs, + towire_gossip_store_channel_amount(tmpctx, *known_amount), 0); + tal_free(pca); + return NULL; + } + + /* Don't know blockheight yet, or not yet deep enough? Don't even ask */ + if (!is_scid_depth_announceable(&scid, blockheight)) { + /* Don't expect to be more than 12 blocks behind! */ + if (blockheight != 0 + && short_channel_id_blocknum(&scid) > blockheight + 12) { + return tal_fmt(ctx, + "Bad gossip order: ignoring channel_announcement %s at blockheight %u", + short_channel_id_to_str(tmpctx, &scid), + blockheight); + } + + if (!map_add(&gm->early_ann_map, scid, pca)) { + /* Already pending? Ignore */ + tal_free(pca); + return NULL; + } + + /* We will retry in gossip_manage_new_block */ + return NULL; + } + + status_debug("channel_announcement: Adding %s to pending...", + short_channel_id_to_str(tmpctx, &scid)); + if (!map_add(&gm->pending_ann_map, scid, pca)) { + /* Already pending? Ignore */ + tal_free(pca); + return NULL; + } + + /* Ask lightningd about this scid: see + * gossmap_manage_handle_get_txout_reply */ + daemon_conn_send(gm->daemon->master, + take(towire_gossipd_get_txout(NULL, &scid))); + return NULL; +} + +/*~ We queue incoming channel_announcement pending confirmation from lightningd + * that it really is an unspent output. Here's its reply. */ +void gossmap_manage_handle_get_txout_reply(struct gossmap_manage *gm, const u8 *msg) +{ + struct short_channel_id scid; + u8 *outscript; + struct amount_sat sat; + struct pending_cannounce *pca; + + if (!fromwire_gossipd_get_txout_reply(msg, msg, &scid, &sat, &outscript)) + master_badmsg(WIRE_GOSSIPD_GET_TXOUT_REPLY, msg); + + status_debug("channel_announcement: got reply for %s...", + short_channel_id_to_str(tmpctx, &scid)); + + pca = map_del(&gm->pending_ann_map, scid); + if (!pca) { + /* If we looking specifically for this, we no longer + * are (but don't penalize sender: we don't know if it was + * good or bad). */ + remove_unknown_scid(gm->daemon->seeker, &scid, true); + /* Was it deleted because we saw channel close? */ + if (!in_txout_failures(gm->txf, scid)) + status_broken("get_txout_reply with unknown scid %s?", + short_channel_id_to_str(tmpctx, &scid)); + return; + } + + /* BOLT #7: + * + * The receiving node: + *... + * - if the `short_channel_id`'s output... is spent: + * - MUST ignore the message. + */ + if (tal_count(outscript) == 0) { + peer_warning(gm, pca->source_peer, + "channel_announcement: no unspent txout %s", + short_channel_id_to_str(tmpctx, &scid)); + goto bad; + } + + if (!memeq(outscript, tal_bytelen(outscript), + pca->scriptpubkey, tal_bytelen(pca->scriptpubkey))) { + peer_warning(gm, pca->source_peer, + "channel_announcement: txout %s expected %s, got %s", + short_channel_id_to_str(tmpctx, &scid), + tal_hex(tmpctx, pca->scriptpubkey), + tal_hex(tmpctx, outscript)); + goto bad; + } + + /* Set with timestamp 0 (we will update once we have a channel_update) */ + gossip_store_add(gm->daemon->gs, pca->channel_announcement, 0); + gossip_store_add(gm->daemon->gs, + towire_gossip_store_channel_amount(tmpctx, sat), 0); + tal_free(pca); + + /* If we looking specifically for this, we no longer are. */ + remove_unknown_scid(gm->daemon->seeker, &scid, true); + + /* When all pending requests are done, we reconsider queued messages */ + reprocess_queued_msgs(gm); + + return; + +bad: + tal_free(pca); + txout_failures_add(gm->txf, scid); + /* If we looking specifically for this, we no longer are. */ + remove_unknown_scid(gm->daemon->seeker, &scid, false); +} + +/* This is called both from when we receive the channel update, and if + * we had to defer. */ +static const char *process_channel_update(const tal_t *ctx, + struct gossmap_manage *gm, + struct short_channel_id scid, + const secp256k1_ecdsa_signature *signature, + u8 message_flags, + u8 channel_flags, + u16 cltv_expiry_delta, + struct amount_msat htlc_minimum_msat, + struct amount_msat htlc_maximum_msat, + u32 fee_base_msat, + u32 fee_proportional_millionths, + u32 timestamp, + const u8 *update, + const struct node_id *source_peer) +{ + struct gossmap_chan *chan; + struct node_id node_id, remote_id; + const char *err; + int dir = (channel_flags & ROUTING_FLAGS_DIRECTION); + struct gossmap *gossmap = gossmap_manage_get_gossmap(gm); + + chan = gossmap_find_chan(gossmap, &scid); + if (!chan) { + /* Did we explicitly reject announce? Ignore completely. */ + if (in_txout_failures(gm->txf, scid)) + return NULL; + + /* Seeker may want to ask about this. */ + query_unknown_channel(gm->daemon, source_peer, scid); + + /* Don't send them warning, it can happen. */ + bad_gossip(source_peer, + tal_fmt(tmpctx, "Unknown channel %s", + short_channel_id_to_str(tmpctx, &scid))); + return NULL; + } + + /* Now we know node, we can check signature. */ + gossmap_node_get_id(gossmap, + gossmap_nth_node(gossmap, chan, dir), + &node_id); + + err = sigcheck_channel_update(ctx, &node_id, signature, update); + if (err) + return err; + + /* Don't allow private updates on public channels! */ + if (message_flags & ROUTING_OPT_DONT_FORWARD) { + return tal_fmt(ctx, "Do not set DONT_FORWARD on public channel_updates (%s)", + short_channel_id_to_str(tmpctx, &scid)); + } + + /* Do we have same or earlier update? */ + if (gossmap_chan_set(chan, dir)) { + u32 prev_timestamp + = gossip_store_get_timestamp(gm->daemon->gs, chan->cupdate_off[dir]); + if (prev_timestamp >= timestamp) { + /* Too old, ignore */ + return NULL; + } + } else { + /* Is this the first update in either direction? If so, + * rewrite channel_announcement so timestamp is correct. */ + if (!gossmap_chan_set(chan, dir)) + gossip_store_set_timestamp(gm->daemon->gs, chan->cann_off, timestamp); + } + + /* OK, apply the new one */ + gossip_store_add(gm->daemon->gs, update, timestamp); + + /* Now delete old */ + if (gossmap_chan_set(chan, dir)) + gossip_store_del(gm->daemon->gs, chan->cupdate_off[dir], WIRE_CHANNEL_UPDATE); + + /* Is this an update for an incoming channel? If so, keep lightningd updated */ + gossmap_node_get_id(gossmap, + gossmap_nth_node(gossmap, chan, !dir), + &remote_id); + if (node_id_eq(&remote_id, &gm->daemon->id)) { + tell_lightningd_peer_update(gm->daemon, source_peer, + scid, fee_base_msat, + fee_proportional_millionths, + cltv_expiry_delta, htlc_minimum_msat, + htlc_maximum_msat); + } + + status_peer_debug(source_peer, + "Received channel_update for channel %s/%d now %s", + type_to_string(tmpctx, struct short_channel_id, + &scid), + dir, + channel_flags & ROUTING_FLAGS_DISABLED ? "DISABLED" : "ACTIVE"); + return NULL; +} + +const char *gossmap_manage_channel_update(const tal_t *ctx, + struct gossmap_manage *gm, + const u8 *update TAKES, + const struct node_id *source_peer TAKES) +{ + secp256k1_ecdsa_signature signature; + struct short_channel_id scid; + u32 timestamp; + u8 message_flags, channel_flags; + u16 cltv_expiry_delta; + struct amount_msat htlc_minimum_msat, htlc_maximum_msat; + u32 fee_base_msat; + u32 fee_proportional_millionths; + struct bitcoin_blkid chain_hash; + struct gossmap *gossmap = gossmap_manage_get_gossmap(gm); + + if (taken(update)) + tal_steal(tmpctx, update); + + if (taken(source_peer)) + tal_steal(tmpctx, source_peer); + + if (!fromwire_channel_update(update, &signature, + &chain_hash, &scid, + ×tamp, &message_flags, + &channel_flags, &cltv_expiry_delta, + &htlc_minimum_msat, &fee_base_msat, + &fee_proportional_millionths, + &htlc_maximum_msat)) { + return tal_fmt(ctx, "channel_update: malformed %s", + tal_hex(tmpctx, update)); + } + + /* Don't accept ancient or far-future timestamps. */ + if (!timestamp_reasonable(gm->daemon, timestamp)) + return NULL; + + /* Still waiting? */ + if (map_get(&gm->pending_ann_map, scid)) { + enqueue_cupdate(&gm->pending_cupdates, + scid, + &signature, + message_flags, + channel_flags, + cltv_expiry_delta, + htlc_minimum_msat, + htlc_maximum_msat, + fee_base_msat, + fee_proportional_millionths, + timestamp, + take(update), + source_peer); + return NULL; + } + + /* Too early? */ + if (map_get(&gm->early_ann_map, scid)) { + enqueue_cupdate(&gm->early_cupdates, + scid, + &signature, + message_flags, + channel_flags, + cltv_expiry_delta, + htlc_minimum_msat, + htlc_maximum_msat, + fee_base_msat, + fee_proportional_millionths, + timestamp, + take(update), + source_peer); + return NULL; + } + + /* Private channel_updates are not always marked as such. So check if it's an unknown + * channel, and signed by the peer itself. */ + if (!gossmap_find_chan(gossmap, &scid) + && source_peer + && sigcheck_channel_update(tmpctx, source_peer, &signature, update) == NULL) { + tell_lightningd_peer_update(gm->daemon, source_peer, + scid, fee_base_msat, + fee_proportional_millionths, + cltv_expiry_delta, htlc_minimum_msat, + htlc_maximum_msat); + return NULL; + } + + return process_channel_update(ctx, gm, scid, &signature, + message_flags, channel_flags, + cltv_expiry_delta, + htlc_minimum_msat, + htlc_maximum_msat, + fee_base_msat, + fee_proportional_millionths, + timestamp, update, source_peer); +} + +static void process_node_announcement(struct gossmap_manage *gm, + const struct gossmap_node *node, + u32 timestamp, + const struct node_id *node_id, + const u8 *nannounce, + const struct node_id *source_peer) +{ + /* Do we have a later one? If so, ignore */ + if (gossmap_node_announced(node)) { + u32 prev_timestamp + = gossip_store_get_timestamp(gm->daemon->gs, node->nann_off); + if (prev_timestamp >= timestamp) { + /* Too old, ignore */ + return; + } + } + + /* OK, apply the new one */ + gossip_store_add(gm->daemon->gs, nannounce, timestamp); + + /* Now delete old */ + if (gossmap_node_announced(node)) + gossip_store_del(gm->daemon->gs, node->nann_off, WIRE_NODE_ANNOUNCEMENT); + + status_peer_debug(source_peer, + "Received node_announcement for node %s", + type_to_string(tmpctx, struct node_id, node_id)); +} + +const char *gossmap_manage_node_announcement(const tal_t *ctx, + struct gossmap_manage *gm, + const u8 *nannounce TAKES, + const struct node_id *source_peer TAKES) +{ + secp256k1_ecdsa_signature signature; + u32 timestamp; + struct node_id node_id; + u8 rgb_color[3]; + u8 alias[32]; + u8 *features, *addresses; + struct wireaddr *wireaddrs; + struct tlv_node_ann_tlvs *na_tlv; + struct gossmap_node *node; + const char *err; + struct gossmap *gossmap = gossmap_manage_get_gossmap(gm); + + if (taken(nannounce)) + tal_steal(tmpctx, nannounce); + + if (taken(source_peer)) + tal_steal(tmpctx, source_peer); + + if (!fromwire_node_announcement(tmpctx, nannounce, + &signature, &features, ×tamp, + &node_id, rgb_color, alias, + &addresses, + &na_tlv)) { + /* BOLT #7: + * + * - if `node_id` is NOT a valid compressed public key: + * - SHOULD send a `warning`. + * - MAY close the connection. + * - MUST NOT process the message further. + */ + return tal_fmt(ctx, "node_announcement: malformed %s", + tal_hex(tmpctx, nannounce)); + } + + wireaddrs = fromwire_wireaddr_array(tmpctx, addresses); + if (!wireaddrs) { + /* BOLT #7: + * + * - if `addrlen` is insufficient to hold the address + * descriptors of the known types: + * - SHOULD send a `warning`. + * - MAY close the connection. + */ + return tal_fmt(ctx, + "node_announcement: malformed wireaddrs %s in %s", + tal_hex(tmpctx, wireaddrs), + tal_hex(tmpctx, nannounce)); + } + + err = sigcheck_node_announcement(ctx, &node_id, &signature, + nannounce); + if (err) + return err; + + node = gossmap_find_node(gossmap, &node_id); + if (!node) { + /* Still waiting for some channel_announcement? */ + if (!map_empty(&gm->pending_ann_map) + || !map_empty(&gm->early_ann_map)) { + enqueue_nannounce(&gm->pending_nannounces, + &node_id, + timestamp, + take(nannounce), + source_peer); + return NULL; + } + + /* Seeker may want to ask about this. */ + query_unknown_node(gm->daemon, source_peer, &node_id); + + /* Don't complain to them: this can happen. */ + bad_gossip(source_peer, + tal_fmt(tmpctx, + "node_announcement: unknown node %s", + node_id_to_hexstr(tmpctx, &node_id))); + return NULL; + } + + process_node_announcement(gm, node, timestamp, &node_id, nannounce, source_peer); + return NULL; +} + +static void process_pending_cupdate(struct gossmap_manage *gm, + struct pending_cupdate *pcu) +{ + const char *err; + + err = process_channel_update(tmpctx, gm, + pcu->scid, + &pcu->signature, + pcu->message_flags, + pcu->channel_flags, + pcu->cltv_expiry_delta, + pcu->htlc_minimum_msat, + pcu->htlc_maximum_msat, + pcu->fee_base_msat, + pcu->fee_proportional_millionths, + pcu->timestamp, + pcu->update, + pcu->source_peer); + if (err) + peer_warning(gm, pcu->source_peer, + "channel_update: %s", err); +} + +/* No channel_announcement now pending, so process every update which was waiting. */ +static void reprocess_pending_cupdates(struct gossmap_manage *gm) +{ + /* Grab current array and reset to empty */ + struct pending_cupdate **pcus = gm->pending_cupdates; + + gm->pending_cupdates = tal_arr(gm, struct pending_cupdate *, 0); + + /* Now we can canonically process any pending channel_updates */ + for (size_t i = 0; i < tal_count(pcus); i++) + process_pending_cupdate(gm, pcus[i]); + + tal_free(pcus); +} + +/* No channel_announcement are early, so process every update which was for those. */ +static void reprocess_early_cupdates(struct gossmap_manage *gm) +{ + /* Grab current array and reset to empty */ + struct pending_cupdate **pcus = gm->early_cupdates; + + gm->early_cupdates = tal_arr(gm, struct pending_cupdate *, 0); + + for (size_t i = 0; i < tal_count(pcus); i++) { + /* Is announcement now pending? Add directly to pending queue. */ + if (map_get(&gm->pending_ann_map, pcus[i]->scid)) { + tal_arr_expand(&gm->pending_cupdates, + tal_steal(gm->pending_cupdates, pcus[i])); + continue; + } + + process_pending_cupdate(gm, pcus[i]); + } + tal_free(pcus); +} + +static void reprocess_queued_msgs(struct gossmap_manage *gm) +{ + bool pending_ann_empty, early_ann_empty; + struct gossmap *gossmap = gossmap_manage_get_gossmap(gm); + + pending_ann_empty = map_empty(&gm->pending_ann_map); + early_ann_empty = map_empty(&gm->early_ann_map); + + if (pending_ann_empty) { + reprocess_pending_cupdates(gm); + /* This should have been final! */ + assert(map_empty(&gm->pending_ann_map)); + } + + if (early_ann_empty) { + /* reprocess_pending_cupdates should not have added any! */ + assert(map_empty(&gm->early_ann_map)); + reprocess_early_cupdates(gm); + /* Won't add any more */ + assert(map_empty(&gm->early_ann_map)); + } + + /* Nothing at all outstanding? All node_announcements can now be processed */ + if (early_ann_empty && pending_ann_empty) { + struct pending_nannounce **pnas = gm->pending_nannounces; + + gm->pending_nannounces = tal_arr(gm, struct pending_nannounce *, 0); + + for (size_t i = 0; i < tal_count(pnas); i++) { + struct gossmap_node *node; + + node = gossmap_find_node(gossmap, &pnas[i]->node_id); + if (!node) { + /* Seeker may want to ask about this. */ + query_unknown_node(gm->daemon, + pnas[i]->source_peer, &pnas[i]->node_id); + + /* Don't complain to them: this can happen. */ + bad_gossip(pnas[i]->source_peer, + tal_fmt(tmpctx, + "node_announcement: unknown node %s", + node_id_to_hexstr(tmpctx, &pnas[i]->node_id))); + continue; + } + + process_node_announcement(gm, node, + pnas[i]->timestamp, + &pnas[i]->node_id, + pnas[i]->nannounce, + pnas[i]->source_peer); + } + + /* Won't add any new ones */ + assert(map_empty(&gm->pending_ann_map)); + assert(map_empty(&gm->early_ann_map)); + + tal_free(pnas); + } +} + +static void kill_spent_channel(struct gossmap_manage *gm, + struct gossmap *gossmap, + struct short_channel_id scid) +{ + struct gossmap_chan *chan; + + chan = gossmap_find_chan(gossmap, &scid); + if (!chan) { + status_broken("Dying channel %s already deleted?", + type_to_string(tmpctx, struct short_channel_id, &scid)); + return; + } + + status_debug("Deleting channel %s due to the funding outpoint being " + "spent", + type_to_string(tmpctx, struct short_channel_id, &scid)); + + remove_channel(gm, gossmap, chan, scid); +} + +void gossmap_manage_new_block(struct gossmap_manage *gm, u32 new_blockheight) +{ + u64 idx; + struct gossmap *gossmap = gossmap_manage_get_gossmap(gm); + + for (struct pending_cannounce *pca = uintmap_first(&gm->early_ann_map.map, &idx); + pca != NULL; + pca = uintmap_after(&gm->early_ann_map.map, &idx)) { + struct short_channel_id scid; + scid.u64 = idx; + + /* Stop when we are at unreachable heights */ + if (!is_scid_depth_announceable(&scid, new_blockheight)) + break; + + map_del(&gm->early_ann_map, scid); + + if (!map_add(&gm->pending_ann_map, scid, pca)) { + /* Already pending? Ignore */ + tal_free(pca); + continue; + } + + status_debug("gossmap_manage: new block, adding %s to pending...", + short_channel_id_to_str(tmpctx, &scid)); + + /* Ask lightningd about this scid: see + * gossmap_manage_handle_get_txout_reply */ + daemon_conn_send(gm->daemon->master, + take(towire_gossipd_get_txout(NULL, &scid))); + } + + for (size_t i = 0; i < tal_count(gm->dying_channels); i++) { + if (gm->dying_channels[i].deadline > new_blockheight) + continue; + + kill_spent_channel(gm, gossmap, gm->dying_channels[i].scid); + gossip_store_del(gm->daemon->gs, + gm->dying_channels[i].gossmap_offset, + WIRE_GOSSIP_STORE_CHAN_DYING); + tal_arr_remove(&gm->dying_channels, i); + } +} + +void gossmap_manage_channel_spent(struct gossmap_manage *gm, + u32 blockheight, + struct short_channel_id scid) +{ + struct gossmap_chan *chan; + const struct gossmap_node *me; + const u8 *msg; + struct chan_dying cd; + struct gossmap *gossmap = gossmap_manage_get_gossmap(gm); + + chan = gossmap_find_chan(gossmap, &scid); + if (!chan) + return; + + me = gossmap_find_node(gossmap, &gm->daemon->id); + /* We delete our own channels immediately, since we have local knowledge */ + if (gossmap_nth_node(gossmap, chan, 0) == me + || gossmap_nth_node(gossmap, chan, 1) == me) { + kill_spent_channel(gm, gossmap, scid); + return; + } + + /* BOLT #7: + * - once its funding output has been spent OR reorganized out: + * - SHOULD forget a channel after a 12-block delay. + */ + cd.deadline = blockheight + 12; + cd.scid = scid; + + /* Remember locally so we can kill it in 12 blocks */ + status_debug("channel %s closing soon due" + " to the funding outpoint being spent", + type_to_string(tmpctx, struct short_channel_id, &scid)); + + /* Save to gossip_store in case we restart */ + msg = towire_gossip_store_chan_dying(tmpctx, &cd.scid, cd.deadline); + cd.gossmap_offset = gossip_store_add(gm->daemon->gs, msg, 0); + tal_arr_expand(&gm->dying_channels, cd); + + /* Mark it dying, so we don't gossip it */ + gossip_store_set_flag(gm->daemon->gs, chan->cann_off, + GOSSIP_STORE_DYING_BIT, + WIRE_CHANNEL_ANNOUNCEMENT); + /* Channel updates too! */ + for (int dir = 0; dir < 2; dir++) { + if (!gossmap_chan_set(chan, dir)) + continue; + + gossip_store_set_flag(gm->daemon->gs, + chan->cupdate_off[dir], + GOSSIP_STORE_DYING_BIT, + WIRE_CHANNEL_UPDATE); + } +} + +struct gossmap *gossmap_manage_get_gossmap(struct gossmap_manage *gm) +{ + gossmap_refresh(gm->raw_gossmap, NULL); + return gm->raw_gossmap; +} + +/* BOLT #7: + * - if the `gossip_queries` feature is negotiated: + * - MUST NOT relay any gossip messages it did not generate itself, + * unless explicitly requested. + */ +/* i.e. the strong implication is that we spam our own gossip aggressively! + * "Look at me!" "Look at me!!!!". + */ +/* Statistically, how many peers to we tell about each channel? */ +#define GOSSIP_SPAM_REDUNDANCY 5 + +void gossmap_manage_new_peer(struct gossmap_manage *gm, + const struct node_id *peer) +{ + struct gossmap_node *me; + const u8 *msg; + u64 send_threshold; + struct gossmap *gossmap = gossmap_manage_get_gossmap(gm); + + /* Find ourselves; if no channels, nothing to send */ + me = gossmap_find_node(gossmap, &gm->daemon->id); + if (!me) + return; + + send_threshold = -1ULL; + + /* Just in case we have many peers and not all are connecting or + * some other corner case, send everything to first few. */ + if (peer_node_id_map_count(gm->daemon->peers) > GOSSIP_SPAM_REDUNDANCY + && me->num_chans > GOSSIP_SPAM_REDUNDANCY) { + send_threshold = -1ULL / me->num_chans * GOSSIP_SPAM_REDUNDANCY; + } + + for (size_t i = 0; i < me->num_chans; i++) { + struct gossmap_chan *chan = gossmap_nth_chan(gossmap, me, i, NULL); + + /* We set this so we'll send a fraction of all our channels */ + if (pseudorand_u64() > send_threshold) + continue; + + /* Send channel_announce */ + msg = gossmap_chan_get_announce(NULL, gossmap, chan); + queue_peer_msg(gm->daemon, peer, take(msg)); + + /* Send both channel_updates (if they exist): both help people + * use our channel, so we care! */ + for (int dir = 0; dir < 2; dir++) { + if (!gossmap_chan_set(chan, dir)) + continue; + msg = gossmap_chan_get_update(NULL, gossmap, chan, dir); + queue_peer_msg(gm->daemon, peer, take(msg)); + } + } + + /* If we have one, we should send our own node_announcement */ + msg = gossmap_node_get_announce(NULL, gossmap, me); + if (msg) + queue_peer_msg(gm->daemon, peer, take(msg)); +} + +void gossmap_manage_tell_lightningd_locals(struct daemon *daemon, + struct gossmap_manage *gm) +{ + struct gossmap_node *me; + const u8 *nannounce; + struct gossmap *gossmap = gossmap_manage_get_gossmap(gm); + + /* Find ourselves; if no channels, nothing to send */ + me = gossmap_find_node(gossmap, &gm->daemon->id); + if (!me) + return; + + for (size_t i = 0; i < me->num_chans; i++) { + struct gossmap_chan *chan = gossmap_nth_chan(gossmap, me, i, NULL); + struct short_channel_id scid; + const u8 *cupdate; + + scid = gossmap_chan_scid(gossmap, chan); + cupdate = gossmap_chan_get_update(tmpctx, gossmap, chan, 0); + if (cupdate) + daemon_conn_send(daemon->master, + take(towire_gossipd_init_cupdate(NULL, + &scid, + cupdate))); + cupdate = gossmap_chan_get_update(tmpctx, gossmap, chan, 1); + if (cupdate) + daemon_conn_send(daemon->master, + take(towire_gossipd_init_cupdate(NULL, + &scid, + cupdate))); + } + + /* Tell lightningd about our current node_announcement, if any */ + nannounce = gossmap_node_get_announce(tmpctx, gossmap, me); + if (nannounce) + daemon_conn_send(daemon->master, + take(towire_gossipd_init_nannounce(NULL, + nannounce))); +} + +struct wireaddr *gossmap_manage_get_node_addresses(const tal_t *ctx, + struct gossmap_manage *gm, + const struct node_id *node_id) +{ + struct gossmap_node *node; + u8 *nannounce; + struct node_id id; + secp256k1_ecdsa_signature signature; + u32 timestamp; + u8 *addresses, *features; + u8 rgb_color[3], alias[32]; + struct tlv_node_ann_tlvs *na_tlvs; + struct wireaddr *wireaddrs; + struct gossmap *gossmap = gossmap_manage_get_gossmap(gm); + + node = gossmap_find_node(gossmap, node_id); + if (!node) + return NULL; + + nannounce = gossmap_node_get_announce(tmpctx, gossmap, + node); + if (!nannounce) + return NULL; + + if (!fromwire_node_announcement(tmpctx, nannounce, + &signature, &features, + ×tamp, + &id, rgb_color, alias, + &addresses, + &na_tlvs)) { + status_broken("Bad node_announcement for %s in gossip_store: %s", + node_id_to_hexstr(tmpctx, node_id), + tal_hex(tmpctx, nannounce)); + return NULL; + } + + wireaddrs = fromwire_wireaddr_array(ctx, addresses); + if (!wireaddrs) { + status_broken("Bad wireaddrs in node_announcement in gossip_store: %s", + tal_hex(tmpctx, nannounce)); + return NULL; + } + + return wireaddrs; +} diff --git a/gossipd/gossmap_manage.h b/gossipd/gossmap_manage.h new file mode 100644 index 000000000000..1749f025a83a --- /dev/null +++ b/gossipd/gossmap_manage.h @@ -0,0 +1,126 @@ +#ifndef LIGHTNING_GOSSIPD_GOSSMAP_MANAGE_H +#define LIGHTNING_GOSSIPD_GOSSMAP_MANAGE_H +#include "config.h" + +struct daemon; +struct gossmap_manage; +struct chan_dying; + +struct gossmap_manage *gossmap_manage_new(const tal_t *ctx, + struct daemon *daemon, + struct chan_dying *dying_channels TAKES); + +/** + * gossmap_manage_channel_announcement: process an incoming channel_announcement + * @ctx: tal context for return string + * @gm: the gossmap_manage context + * @announce: the channel_announcement message + * @source_peer: peer who sent this (NULL if it's from lightningd) + * @known_amount: if non-NULL, do not ask lightningd to look up UTXO. + * + * Returns an error string if it wasn't redundant or included. Lightningd + * suppresses lookups if it generated the announcement, partially because it's + * redundant, but also because in our tests the UTXO is often spent by the time + * it processes the lookup! + */ +const char *gossmap_manage_channel_announcement(const tal_t *ctx, + struct gossmap_manage *gm, + const u8 *announce TAKES, + const struct node_id *source_peer TAKES, + const struct amount_sat *known_amount); + + +/** + * gossmap_manage_handle_get_txout_reply: process a txout reply from lightningd + * @gm: the gossmap_manage context + * @msg: the message + * + * Since handle_channel_announcement asks lightning for utxos, + * it gets called back here. + */ +void gossmap_manage_handle_get_txout_reply(struct gossmap_manage *gm, const u8 *msg); + +/** + * gossmap_manage_channel_update: process an incoming channel_update + * @ctx: tal context for return string + * @gm: the gossmap_manage context + * @update: the channel_update message + * @source_peer: optional peer who sent this + * + * Returns an error string if it wasn't redundant or included. + */ +const char *gossmap_manage_channel_update(const tal_t *ctx, + struct gossmap_manage *gm, + const u8 *update TAKES, + const struct node_id *source_peer TAKES); + +/** + * gossmap_manage_node_announcement: process an incoming node_announcement + * @ctx: tal context for return string allocation + * @gm: the gossmap_manage context + * @node_announcement: the node_announcement message + * @source_peer: optional peer who sent this + * + * Returns an error string if it wasn't redundant or included. + */ +const char *gossmap_manage_node_announcement(const tal_t *ctx, + struct gossmap_manage *gm, + const u8 *node_announcement TAKES, + const struct node_id *source_peer TAKES); + +/** + * gossmap_manage_new_block: handle block height update. + * @gm: the gossmap_manage context + * @new_blockheight: the new blockheight + */ +void gossmap_manage_new_block(struct gossmap_manage *gm, u32 new_blockheight); + +/** + * gossmap_manage_channel_spent: handle an UTXO being spent + * @gm: the gossmap_manage context + * @blockheight: the blockheight it was spent at + * @scid: the short_channel_id + * + * lightningd tells us all the possible UTXOs spent every block: most + * don't match channels. + */ +void gossmap_manage_channel_spent(struct gossmap_manage *gm, + u32 blockheight, + struct short_channel_id scid); + +/** + * gossmap_manage_get_gossmap: get the (refreshed!) gossmap + * @gm: the gossmap_manage context + */ +struct gossmap *gossmap_manage_get_gossmap(struct gossmap_manage *gm); + +/** + * gossmap_manage_new_peer: send all our own gossip to this peer. + * @gm: the gossmap_manage context + * @peer: the node_id of the peer. + */ +void gossmap_manage_new_peer(struct gossmap_manage *gm, + const struct node_id *peer); + +/** + * gossmap_manage_get_node_addresses: get addresses for this node. + * @ctx: the allocation context + * @gm: the gossmap_manage context + * @node_id: the node_id to look up + * + * Returns NULL if we don't have node_announcement for it. + */ +struct wireaddr *gossmap_manage_get_node_addresses(const tal_t *ctx, + struct gossmap_manage *gm, + const struct node_id *node_id); + +/** + * gossmap_manage_tell_lightningd_locals: tell lightningd our latest updates. + * @daemon: the gossip daemon + * @gm: the gossmap_manage context + * + * Done before we reply to gossipd_init. + */ +void gossmap_manage_tell_lightningd_locals(struct daemon *daemon, + struct gossmap_manage *gm); +#endif /* LIGHTNING_GOSSIPD_GOSSMAP_MANAGE_H */ diff --git a/gossipd/queries.c b/gossipd/queries.c index 61d70293a893..4f11cc53d789 100644 --- a/gossipd/queries.c +++ b/gossipd/queries.c @@ -6,13 +6,15 @@ #include #include #include +#include #include #include #include +#include #include #include +#include #include -#include #include static u32 dev_max_encoding_bytes = -1U; @@ -147,7 +149,7 @@ bool query_short_channel_ids(struct daemon *daemon, msg = towire_query_short_channel_ids(NULL, &chainparams->genesis_blockhash, encoded, tlvs); - queue_peer_msg(peer, take(msg)); + queue_peer_msg(daemon, &peer->id, take(msg)); peer->scid_query_outstanding = true; peer->scid_query_cb = cb; @@ -320,7 +322,7 @@ static void send_reply_channel_range(struct peer *peer, first_blocknum, number_of_blocks, final, encoded_scids, tlvs); - queue_peer_msg(peer, take(msg)); + queue_peer_msg(peer->daemon, &peer->id, take(msg)); } /* Helper to get non-signature, non-timestamp parts of (valid!) channel_update */ @@ -367,23 +369,46 @@ static u32 crc32_of_update(const u8 *channel_update) return sum; } -static void get_checksum_and_timestamp(struct routing_state *rstate, - const struct chan *chan, - int direction, - u32 *tstamp, u32 *csum) +/* BOLT #7: + * Where: + * * `timestamp_node_id_1` is the timestamp of the `channel_update` for `node_id_1`, or 0 if there was no `channel_update` from that node. + * * `timestamp_node_id_2` is the timestamp of the `channel_update` for `node_id_2`, or 0 if there was no `channel_update` from that node. + */ +static u32 get_timestamp(struct gossmap *gossmap, + const struct gossmap_chan *chan, + int dir) { - const struct half_chan *hc = &chan->half[direction]; + u32 timestamp; + if (!gossmap_chan_set(chan, dir)) + return 0; - if (!is_halfchan_defined(hc)) { - *tstamp = *csum = 0; - } else { - const u8 *update = gossip_store_get(tmpctx, rstate->gs, - hc->bcast.index); - *tstamp = hc->bcast.timestamp; - *csum = crc32_of_update(update); - } + gossmap_chan_get_update_details(gossmap, chan, dir, ×tamp, + NULL, NULL, NULL, NULL, NULL, NULL); + return timestamp; +} + +/* BOLT #7: + * Where: + * * `checksum_node_id_1` is the checksum of the `channel_update` for + * `node_id_1`, or 0 if there was no `channel_update` from that + * node. + * * `checksum_node_id_2` is the checksum of the `channel_update` for + * `node_id_2`, or 0 if there was no `channel_update` from that + * node. + */ +static u32 get_checksum(struct gossmap *gossmap, + const struct gossmap_chan *chan, + int dir) +{ + u8 *cupdate; + + cupdate = gossmap_chan_get_update(tmpctx, gossmap, chan, dir); + if (!cupdate) + return 0; + return crc32_of_update(cupdate); } + /* FIXME: This assumes that the tlv type encodes into 1 byte! */ static size_t tlv_overhead(size_t num_entries, size_t size) { @@ -437,15 +462,15 @@ static size_t max_entries(enum query_option_flags query_option_flags) /* This gets all the scids they asked for, and optionally the timestamps and checksums */ static struct short_channel_id *gather_range(const tal_t *ctx, - struct routing_state *rstate, + struct daemon *daemon, u32 first_blocknum, u32 number_of_blocks, enum query_option_flags query_option_flags, struct channel_update_timestamps **tstamps, struct channel_update_checksums **csums) { - struct short_channel_id scid, *scids; + struct short_channel_id *scids; u32 end_block; - bool scid_ok; + struct gossmap *gossmap = gossmap_manage_get_gossmap(daemon->gm); scids = tal_arr(ctx, struct short_channel_id, 0); if (query_option_flags & QUERY_ADD_TIMESTAMPS) @@ -457,16 +482,6 @@ static struct short_channel_id *gather_range(const tal_t *ctx, else *csums = NULL; - /* Avoid underflow: we don't use block 0 anyway */ - if (first_blocknum == 0) - scid_ok = mk_short_channel_id(&scid, 1, 0, 0); - else - scid_ok = mk_short_channel_id(&scid, first_blocknum, 0, 0); - scid.u64--; - /* Out of range? No blocks then. */ - if (!scid_ok) - return NULL; - if (number_of_blocks == 0) return NULL; @@ -475,39 +490,44 @@ static struct short_channel_id *gather_range(const tal_t *ctx, if (end_block < first_blocknum) end_block = UINT_MAX; - /* We keep a `uintmap` of `short_channel_id` to `struct chan *`. - * Unlike a htable, it's efficient to iterate through, but it only - * works because each short_channel_id is basically a 64-bit unsigned - * integer. - * - * First we iterate and gather all the short channel ids. */ - while (uintmap_after(&rstate->chanmap, &scid.u64)) { - struct chan *chan; - struct channel_update_timestamps ts; - struct channel_update_checksums cs; + /* We used to maintain a uintmap of channels by scid, but + * we no longer do, making this more expensive. But still + * not too bad, since it's usually in-mem */ + for (size_t i = 0; i < gossmap_max_chan_idx(gossmap); i++) { + struct gossmap_chan *chan = gossmap_chan_byidx(gossmap, i); + struct short_channel_id scid; - if (short_channel_id_blocknum(&scid) > end_block) - break; + if (!chan) + continue; - /* FIXME: Store csum in header. */ - chan = get_channel(rstate, &scid); - tal_arr_expand(&scids, scid); + /* By policy, we don't give announcements here with no + * channel_updates */ + if (!gossmap_chan_set(chan, 0) && !gossmap_chan_set(chan, 1)) { + continue; + } - /* Don't calc csums if we don't even care */ - if (!(query_option_flags - & (QUERY_ADD_TIMESTAMPS|QUERY_ADD_CHECKSUMS))) + scid = gossmap_chan_scid(gossmap, chan); + if (short_channel_id_blocknum(&scid) < first_blocknum + || short_channel_id_blocknum(&scid) > end_block) { continue; + } - get_checksum_and_timestamp(rstate, chan, 0, - &ts.timestamp_node_id_1, - &cs.checksum_node_id_1); - get_checksum_and_timestamp(rstate, chan, 1, - &ts.timestamp_node_id_2, - &cs.checksum_node_id_2); - if (query_option_flags & QUERY_ADD_TIMESTAMPS) + tal_arr_expand(&scids, scid); + + if (*tstamps) { + struct channel_update_timestamps ts; + + ts.timestamp_node_id_1 = get_timestamp(gossmap, chan, 0); + ts.timestamp_node_id_2 = get_timestamp(gossmap, chan, 1); tal_arr_expand(tstamps, ts); - if (query_option_flags & QUERY_ADD_CHECKSUMS) + } + + if (*csums) { + struct channel_update_checksums cs; + cs.checksum_node_id_1 = get_checksum(gossmap, chan, 0); + cs.checksum_node_id_2 = get_checksum(gossmap, chan, 1); tal_arr_expand(csums, cs); + } } return scids; @@ -522,13 +542,13 @@ static void queue_channel_ranges(struct peer *peer, u32 first_blocknum, u32 number_of_blocks, enum query_option_flags query_option_flags) { - struct routing_state *rstate = peer->daemon->rstate; + struct daemon *daemon = peer->daemon; struct channel_update_timestamps *tstamps; struct channel_update_checksums *csums; struct short_channel_id *scids; size_t off, limit; - scids = gather_range(tmpctx, rstate, first_blocknum, number_of_blocks, + scids = gather_range(tmpctx, daemon, first_blocknum, number_of_blocks, query_option_flags, &tstamps, &csums); limit = max_entries(query_option_flags); @@ -617,7 +637,7 @@ const u8 *handle_query_channel_range(struct peer *peer, const u8 *msg) &chain_hash)); u8 *end = towire_reply_channel_range(NULL, &chain_hash, first_blocknum, number_of_blocks, false, NULL, NULL); - queue_peer_msg(peer, take(end)); + queue_peer_msg(peer->daemon, &peer->id, take(end)); return NULL; } @@ -929,9 +949,11 @@ static void uniquify_node_ids(struct node_id **ids) * it's finished all of them. */ static bool maybe_send_query_responses_peer(struct peer *peer) { - struct routing_state *rstate = peer->daemon->rstate; + struct daemon *daemon = peer->daemon; size_t i, num; bool sent = false; + const u8 *msg; + struct gossmap *gossmap = gossmap_manage_get_gossmap(daemon->gm); /* BOLT #7: * @@ -940,9 +962,11 @@ static bool maybe_send_query_responses_peer(struct peer *peer) /* Search for next short_channel_id we know about. */ num = tal_count(peer->scid_queries); for (i = peer->scid_query_idx; !sent && i < num; i++) { - struct chan *chan; + struct gossmap_chan *chan; + struct gossmap_node *node; + struct node_id node_id; - chan = get_channel(rstate, &peer->scid_queries[i]); + chan = gossmap_find_chan(gossmap, &peer->scid_queries[i]); if (!chan) continue; @@ -951,7 +975,8 @@ static bool maybe_send_query_responses_peer(struct peer *peer) * - MUST reply with a `channel_announcement` */ if (peer->scid_query_flags[i] & SCID_QF_ANNOUNCE) { - queue_peer_from_store(peer, &chan->bcast); + msg = gossmap_chan_get_announce(NULL, gossmap, chan); + queue_peer_msg(daemon, &peer->id, take(msg)); sent = true; } @@ -965,13 +990,15 @@ static bool maybe_send_query_responses_peer(struct peer *peer) * - MUST reply with the latest `channel_update` for * `node_id_2` */ if ((peer->scid_query_flags[i] & SCID_QF_UPDATE1) - && is_halfchan_defined(&chan->half[0])) { - queue_peer_from_store(peer, &chan->half[0].bcast); + && gossmap_chan_set(chan, 0)) { + msg = gossmap_chan_get_update(NULL, gossmap, chan, 0); + queue_peer_msg(daemon, &peer->id, take(msg)); sent = true; } if ((peer->scid_query_flags[i] & SCID_QF_UPDATE2) - && is_halfchan_defined(&chan->half[1])) { - queue_peer_from_store(peer, &chan->half[1].bcast); + && gossmap_chan_set(chan, 1)) { + msg = gossmap_chan_get_update(NULL, gossmap, chan, 1); + queue_peer_msg(daemon, &peer->id, take(msg)); sent = true; } @@ -985,12 +1012,16 @@ static bool maybe_send_query_responses_peer(struct peer *peer) * - MUST reply with the latest `node_announcement` for * `node_id_2` */ /* Save node ids for later transmission of node_announcement */ - if (peer->scid_query_flags[i] & SCID_QF_NODE1) - tal_arr_expand(&peer->scid_query_nodes, - chan->nodes[0]->id); - if (peer->scid_query_flags[i] & SCID_QF_NODE2) - tal_arr_expand(&peer->scid_query_nodes, - chan->nodes[1]->id); + if (peer->scid_query_flags[i] & SCID_QF_NODE1) { + node = gossmap_nth_node(gossmap, chan, 0); + gossmap_node_get_id(gossmap, node, &node_id); + tal_arr_expand(&peer->scid_query_nodes, node_id); + } + if (peer->scid_query_flags[i] & SCID_QF_NODE2) { + node = gossmap_nth_node(gossmap, chan, 1); + gossmap_node_get_id(gossmap, node, &node_id); + tal_arr_expand(&peer->scid_query_nodes, node_id); + } } /* Just finished channels? Remove duplicate nodes. */ @@ -1021,15 +1052,16 @@ static bool maybe_send_query_responses_peer(struct peer *peer) * node_announcement to send. */ num = tal_count(peer->scid_query_nodes); for (i = peer->scid_query_nodes_idx; !sent && i < num; i++) { - const struct node *n; + const struct gossmap_node *n; /* Not every node announces itself (we know it exists because * of a channel_announcement, however) */ - n = get_node(rstate, &peer->scid_query_nodes[i]); - if (!n || !n->bcast.index) + n = gossmap_find_node(gossmap, &peer->scid_query_nodes[i]); + if (!n || !gossmap_node_announced(n)) continue; - queue_peer_from_store(peer, &n->bcast); + msg = gossmap_node_get_announce(NULL, gossmap, n); + queue_peer_msg(daemon, &peer->id, take(msg)); sent = true; } peer->scid_query_nodes_idx = i; @@ -1052,7 +1084,7 @@ static bool maybe_send_query_responses_peer(struct peer *peer) u8 *end = towire_reply_short_channel_ids_end(peer, &chainparams->genesis_blockhash, true); - queue_peer_msg(peer, take(end)); + queue_peer_msg(peer->daemon, &peer->id, take(end)); /* We're done! Clean up so we simply pass-through next time. */ peer->scid_queries = tal_free(peer->scid_queries); @@ -1107,7 +1139,7 @@ bool query_channel_range(struct daemon *daemon, msg = towire_query_channel_range(NULL, &chainparams->genesis_blockhash, first_blocknum, number_of_blocks, tlvs); - queue_peer_msg(peer, take(msg)); + queue_peer_msg(peer->daemon, &peer->id, take(msg)); peer->range_first_blocknum = first_blocknum; peer->range_end_blocknum = first_blocknum + number_of_blocks; peer->range_blocks_outstanding = number_of_blocks; diff --git a/gossipd/queries.h b/gossipd/queries.h index 727f6ef1023f..b6748d6d7b1d 100644 --- a/gossipd/queries.h +++ b/gossipd/queries.h @@ -53,10 +53,6 @@ bool query_short_channel_ids(struct daemon *daemon, const u8 *query_flags, void (*cb)(struct peer *peer_, bool complete)); -struct io_plan *dev_query_channel_range(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg); - /* This is a testing hack to allow us to artificially lower the maximum bytes * of short_channel_ids we'll encode, using dev_set_max_scids_encode_size. */ void dev_set_max_scids_encode_size(struct daemon *daemon, const u8 *msg); diff --git a/gossipd/routing.c b/gossipd/routing.c deleted file mode 100644 index 65f620b0fd59..000000000000 --- a/gossipd/routing.c +++ /dev/null @@ -1,2297 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef SUPERVERBOSE -#define SUPERVERBOSE(...) -#endif - -/* 365.25 * 24 * 60 / 10 */ -#define BLOCKS_PER_YEAR 52596 - -struct pending_spam_node_announce { - u8 *node_announcement; - u32 index; -}; - -struct pending_node_announce { - struct routing_state *rstate; - struct node_id nodeid; - size_t refcount; - u8 *node_announcement; - u32 timestamp; - u32 index; - /* If non-NULL this is peer to credit it with */ - struct node_id *source_peer; - /* required for loading gossip store */ - struct pending_spam_node_announce spam; -}; - -/* As per the below BOLT #7 quote, we delay forgetting a channel until 12 - * blocks after we see it close. This gives time for splicing (or even other - * opens) to replace the channel, and broadcast it after 6 blocks. */ -struct dying_channel { - struct short_channel_id scid; - u32 deadline_blockheight; - /* Where the dying_channel marker is in the store. */ - struct broadcastable marker; -}; - -/* We consider a reasonable gossip rate to be 2 per day, with burst of - * 4 per day. So we use a granularity of one hour. */ -#define TOKENS_PER_MSG 12 -#define TOKEN_MAX (12 * 4) - -static u8 update_tokens(const struct routing_state *rstate, - u8 tokens, u32 prev_timestamp, u32 new_timestamp) -{ - u64 num_tokens = tokens; - - assert(new_timestamp >= prev_timestamp); - - num_tokens += ((new_timestamp - prev_timestamp) - / GOSSIP_TOKEN_TIME(rstate->dev_fast_gossip)); - if (num_tokens > TOKEN_MAX) - num_tokens = TOKEN_MAX; - return num_tokens; -} - -static bool ratelimit(const struct routing_state *rstate, - u8 *tokens, u32 prev_timestamp, u32 new_timestamp) -{ - *tokens = update_tokens(rstate, *tokens, prev_timestamp, new_timestamp); - - /* Now, if we can afford it, pass this message. */ - if (*tokens >= TOKENS_PER_MSG) { - *tokens -= TOKENS_PER_MSG; - return true; - } - return false; -} - -static const struct node_id * -pending_node_announce_keyof(const struct pending_node_announce *a) -{ - return &a->nodeid; -} - -static bool pending_node_announce_eq(const struct pending_node_announce *pna, - const struct node_id *pc) -{ - return node_id_eq(&pna->nodeid, pc); -} - -HTABLE_DEFINE_TYPE(struct pending_node_announce, pending_node_announce_keyof, - node_map_hash_key, pending_node_announce_eq, - pending_node_map); - -/* We keep around announcements for channels until we have an - * update for them (which gives us their timestamp) */ -struct unupdated_channel { - /* The channel_announcement message */ - const u8 *channel_announce; - /* The short_channel_id */ - struct short_channel_id scid; - /* The ids of the nodes */ - struct node_id id[2]; - /* When we added, so we can discard old ones */ - struct timeabs added; - /* If we loaded from the store, this is where. */ - u32 index; - /* Channel capacity */ - struct amount_sat sat; - /* If non-NULL this is peer to credit it with */ - struct node_id *source_peer; -}; - -static struct unupdated_channel * -get_unupdated_channel(const struct routing_state *rstate, - const struct short_channel_id *scid) -{ - return uintmap_get(&rstate->unupdated_chanmap, scid->u64); -} - -static void destroy_unupdated_channel(struct unupdated_channel *uc, - struct routing_state *rstate) -{ - uintmap_del(&rstate->unupdated_chanmap, uc->scid.u64); -} - -static struct node_map *new_node_map(const tal_t *ctx) -{ - struct node_map *map = tal(ctx, struct node_map); - node_map_init(map); - return map; -} - -/* We use a simple array (with NULL entries) until we have too many. */ -static bool node_uses_chan_map(const struct node *node) -{ - return node->chan_map; -} - -/* When simple array fills, use a htable. */ -static void convert_node_to_chan_map(struct node *node) -{ - assert(!node_uses_chan_map(node)); - node->chan_map = tal(node, struct chan_map); - chan_map_init_sized(node->chan_map, ARRAY_SIZE(node->chan_arr) + 1); - assert(node_uses_chan_map(node)); - for (size_t i = 0; i < ARRAY_SIZE(node->chan_arr); i++) { - chan_map_add(node->chan_map, node->chan_arr[i]); - node->chan_arr[i] = NULL; - } -} - -static void add_chan(struct node *node, struct chan *chan) -{ - if (!node_uses_chan_map(node)) { - for (size_t i = 0; i < ARRAY_SIZE(node->chan_arr); i++) { - if (node->chan_arr[i] == NULL) { - node->chan_arr[i] = chan; - return; - } - } - convert_node_to_chan_map(node); - } - - chan_map_add(node->chan_map, chan); -} - -static struct chan *next_chan_arr(const struct node *node, - struct chan_map_iter *i) -{ - while (i->i.off < ARRAY_SIZE(node->chan_arr)) { - if (node->chan_arr[i->i.off]) - return node->chan_arr[i->i.off]; - i->i.off++; - } - return NULL; -} - -struct chan *first_chan(const struct node *node, struct chan_map_iter *i) -{ - if (!node_uses_chan_map(node)) { - i->i.off = 0; - return next_chan_arr(node, i); - } - - return chan_map_first(node->chan_map, i); -} - -struct chan *next_chan(const struct node *node, struct chan_map_iter *i) -{ - if (!node_uses_chan_map(node)) { - i->i.off++; - return next_chan_arr(node, i); - } - - return chan_map_next(node->chan_map, i); -} - -static void destroy_routing_state(struct routing_state *rstate) -{ - /* Since we omitted destructors on these, clean up manually */ - u64 idx; - for (struct chan *chan = uintmap_first(&rstate->chanmap, &idx); - chan; - chan = uintmap_after(&rstate->chanmap, &idx)) - free_chan(rstate, chan); -} - -/* We don't check this when loading from the gossip_store: that would break - * our canned tests, and usually old gossip is better than no gossip */ -static bool timestamp_reasonable(struct routing_state *rstate, u32 timestamp) -{ - u64 now = gossip_time_now(rstate).ts.tv_sec; - - /* More than one day ahead? */ - if (timestamp > now + 24*60*60) - return false; - /* More than 2 weeks behind? */ - if (timestamp < now - GOSSIP_PRUNE_INTERVAL(rstate->dev_fast_gossip_prune)) - return false; - return true; -} - -static void memleak_help_routing_tables(struct htable *memtable, - struct routing_state *rstate) -{ - struct node *n; - struct node_map_iter nit; - - memleak_scan_htable(memtable, &rstate->nodes->raw); - memleak_scan_htable(memtable, &rstate->pending_node_map->raw); - memleak_scan_htable(memtable, &rstate->pending_cannouncements->raw); - memleak_scan_uintmap(memtable, &rstate->unupdated_chanmap); - - for (n = node_map_first(rstate->nodes, &nit); - n; - n = node_map_next(rstate->nodes, &nit)) { - if (node_uses_chan_map(n)) - memleak_scan_htable(memtable, &n->chan_map->raw); - } -} - -/* Once an hour, or at 10000 entries, we expire old ones */ -static void txout_failure_age(struct routing_state *rstate) -{ - uintmap_clear(&rstate->txout_failures_old); - rstate->txout_failures_old = rstate->txout_failures; - uintmap_init(&rstate->txout_failures); - rstate->num_txout_failures = 0; - - rstate->txout_failure_timer = new_reltimer(&rstate->daemon->timers, - rstate, time_from_sec(3600), - txout_failure_age, rstate); -} - -static void add_to_txout_failures(struct routing_state *rstate, - const struct short_channel_id *scid) -{ - if (uintmap_add(&rstate->txout_failures, scid->u64, true) - && ++rstate->num_txout_failures == 10000) { - tal_free(rstate->txout_failure_timer); - txout_failure_age(rstate); - } -} - -static bool in_txout_failures(struct routing_state *rstate, - const struct short_channel_id *scid) -{ - if (uintmap_get(&rstate->txout_failures, scid->u64)) - return true; - - /* If we were going to expire it, we no longer are. */ - if (uintmap_get(&rstate->txout_failures_old, scid->u64)) { - add_to_txout_failures(rstate, scid); - return true; - } - return false; -} - -struct routing_state *new_routing_state(const tal_t *ctx, - struct daemon *daemon, - const u32 *dev_gossip_time TAKES, - bool dev_fast_gossip, - bool dev_fast_gossip_prune) -{ - struct routing_state *rstate = tal(ctx, struct routing_state); - rstate->daemon = daemon; - rstate->nodes = new_node_map(rstate); - rstate->gs = gossip_store_new(rstate); - rstate->last_timestamp = 0; - rstate->dying_channels = tal_arr(rstate, struct dying_channel, 0); - - rstate->pending_cannouncements = tal(rstate, struct pending_cannouncement_map); - pending_cannouncement_map_init(rstate->pending_cannouncements); - - uintmap_init(&rstate->chanmap); - uintmap_init(&rstate->unupdated_chanmap); - rstate->num_txout_failures = 0; - uintmap_init(&rstate->txout_failures); - uintmap_init(&rstate->txout_failures_old); - txout_failure_age(rstate); - rstate->pending_node_map = tal(ctx, struct pending_node_map); - pending_node_map_init(rstate->pending_node_map); - - if (dev_gossip_time) { - assert(daemon->developer); - rstate->dev_gossip_time = tal(rstate, struct timeabs); - rstate->dev_gossip_time->ts.tv_sec = *dev_gossip_time; - rstate->dev_gossip_time->ts.tv_nsec = 0; - } else - rstate->dev_gossip_time = NULL; - rstate->dev_fast_gossip = dev_fast_gossip; - rstate->dev_fast_gossip_prune = dev_fast_gossip_prune; - tal_add_destructor(rstate, destroy_routing_state); - memleak_add_helper(rstate, memleak_help_routing_tables); - - if (taken(dev_gossip_time)) - tal_free(dev_gossip_time); - - return rstate; -} - - -const struct node_id *node_map_keyof_node(const struct node *n) -{ - return &n->id; -} - -size_t node_map_hash_key(const struct node_id *pc) -{ - return siphash24(siphash_seed(), pc->k, sizeof(pc->k)); -} - -bool node_map_node_eq(const struct node *n, const struct node_id *pc) -{ - return node_id_eq(&n->id, pc); -} - - -static void destroy_node(struct node *node, struct routing_state *rstate) -{ - struct chan_map_iter i; - struct chan *c; - node_map_del(rstate->nodes, node); - - /* These remove themselves from chans[]. */ - while ((c = first_chan(node, &i)) != NULL) - free_chan(rstate, c); -} - -struct node *get_node(struct routing_state *rstate, - const struct node_id *id) -{ - return node_map_get(rstate->nodes, id); -} - -static struct node *new_node(struct routing_state *rstate, - const struct node_id *id) -{ - struct node *n; - - assert(!get_node(rstate, id)); - - n = tal(rstate, struct node); - n->id = *id; - memset(n->chan_arr, 0, sizeof(n->chan_arr)); - n->chan_map = NULL; - broadcastable_init(&n->bcast); - broadcastable_init(&n->rgraph); - n->tokens = TOKEN_MAX; - node_map_add(rstate->nodes, n); - tal_add_destructor2(n, destroy_node, rstate); - - return n; -} - -static bool is_chan_zombie(struct chan *chan) -{ - if (chan->half[0].zombie || chan->half[1].zombie) - return true; - return false; -} - -static bool is_node_zombie(struct node* node) -{ - struct chan_map_iter i; - struct chan *c; - - for (c = first_chan(node, &i); c; c = next_chan(node, &i)) { - if (!is_chan_zombie(c)) - return false; - } - return true; -} - -/* We can *send* a channel_announce for a channel attached to this node: - * we only send once we have a channel_update. */ -bool node_has_broadcastable_channels(const struct node *node) -{ - struct chan_map_iter i; - struct chan *c; - - for (c = first_chan(node, &i); c; c = next_chan(node, &i)) { - if (is_chan_zombie(c)) - continue; - if (is_halfchan_defined(&c->half[0]) - || is_halfchan_defined(&c->half[1])) - return true; - } - return false; -} - -static bool node_announce_predates_channels(const struct node *node) -{ - struct chan_map_iter i; - struct chan *c; - - for (c = first_chan(node, &i); c; c = next_chan(node, &i)) { - /* Zombies don't count! */ - if (is_chan_zombie(c)) - continue; - - if (c->bcast.index < node->bcast.index) - return false; - } - return true; -} - -/* Move this node's announcement to the tail of the gossip_store, to - * make everyone send it again. */ -static void force_node_announce_rexmit(struct routing_state *rstate, - struct node *node) -{ - const u8 *announce; - announce = gossip_store_get(tmpctx, rstate->gs, node->bcast.index); - - u32 initial_bcast_index = node->bcast.index; - gossip_store_delete(rstate->gs, - &node->bcast, - WIRE_NODE_ANNOUNCEMENT); - node->bcast.index = gossip_store_add(rstate->gs, - announce, - node->bcast.timestamp, - false, - false, - false, - NULL); - if (node->rgraph.index == initial_bcast_index){ - node->rgraph.index = node->bcast.index; - } else { - announce = gossip_store_get(tmpctx, rstate->gs, node->rgraph.index); - gossip_store_delete(rstate->gs, - &node->rgraph, - WIRE_NODE_ANNOUNCEMENT); - node->rgraph.index = gossip_store_add(rstate->gs, - announce, - node->rgraph.timestamp, - false, - true, - false, - NULL); - } -} - -static void remove_chan_from_node(struct routing_state *rstate, - struct node *node, const struct chan *chan) -{ - size_t num_chans; - - if (!node_uses_chan_map(node)) { - num_chans = 0; - for (size_t i = 0; i < ARRAY_SIZE(node->chan_arr); i++) { - if (node->chan_arr[i] == chan) - node->chan_arr[i] = NULL; - else if (node->chan_arr[i] != NULL) - num_chans++; - } - } else { - if (!chan_map_del(node->chan_map, chan)) - abort(); - num_chans = chan_map_count(node->chan_map); - } - - /* Last channel? Simply delete node (and associated announce) */ - if (num_chans == 0) { - if (node->rgraph.index != node->bcast.index) - gossip_store_delete(rstate->gs, - &node->rgraph, - WIRE_NODE_ANNOUNCEMENT); - gossip_store_delete(rstate->gs, - &node->bcast, - WIRE_NODE_ANNOUNCEMENT); - tal_free(node); - return; - } - - /* Don't bother if there's no node_announcement */ - if (!node->bcast.index) - return; - - /* Removed only public channel? Remove node announcement. */ - if (!node_has_broadcastable_channels(node)) { - if (node->rgraph.index != node->bcast.index) - gossip_store_delete(rstate->gs, - &node->rgraph, - WIRE_NODE_ANNOUNCEMENT); - gossip_store_delete(rstate->gs, - &node->bcast, - WIRE_NODE_ANNOUNCEMENT); - node->rgraph.index = node->bcast.index = 0; - node->rgraph.timestamp = node->bcast.timestamp = 0; - } else if (node_announce_predates_channels(node)) { - /* node announcement predates all channel announcements? - * Move to end (we could, in theory, move to just past next - * channel_announce, but we don't care that much about spurious - * retransmissions in this corner case */ - force_node_announce_rexmit(rstate, node); - } -} - -/* With --developer, we make sure that free_chan is called on this chan! */ -static void destroy_chan_check(struct chan *chan) -{ - assert(chan->sat.satoshis == (unsigned long)chan); /* Raw: dev-hack */ -} - -static void free_chans_from_node(struct routing_state *rstate, struct chan *chan) -{ - remove_chan_from_node(rstate, chan->nodes[0], chan); - remove_chan_from_node(rstate, chan->nodes[1], chan); - - if (rstate->daemon->developer) - chan->sat.satoshis = (unsigned long)chan; /* Raw: dev-hack */ -} - -/* We used to make this a tal_add_destructor2, but that costs 40 bytes per - * chan, and we only ever explicitly free it anyway. */ -void free_chan(struct routing_state *rstate, struct chan *chan) -{ - free_chans_from_node(rstate, chan); - uintmap_del(&rstate->chanmap, chan->scid.u64); - - tal_free(chan); -} - -static void init_half_chan(struct routing_state *rstate, - struct chan *chan, - int channel_idx) -{ - struct half_chan *c = &chan->half[channel_idx]; - - broadcastable_init(&c->bcast); - broadcastable_init(&c->rgraph); - c->tokens = TOKEN_MAX; - c->zombie = false; -} - -static void bad_gossip_order(const u8 *msg, - const struct node_id *source_peer, - const char *details) -{ - status_peer_debug(source_peer, - "Bad gossip order: %s before announcement %s from %s", - peer_wire_name(fromwire_peektype(msg)), - details, - source_peer ? type_to_string(tmpctx, struct node_id, source_peer) : "local"); -} - -struct chan *new_chan(struct routing_state *rstate, - const struct short_channel_id *scid, - const struct node_id *id1, - const struct node_id *id2, - struct amount_sat satoshis) -{ - struct chan *chan = tal(rstate, struct chan); - int n1idx = node_id_idx(id1, id2); - struct node *n1, *n2; - - if (rstate->daemon->developer) - tal_add_destructor(chan, destroy_chan_check); - - /* We should never add a channel twice */ - assert(!uintmap_get(&rstate->chanmap, scid->u64)); - - /* Create nodes on demand */ - n1 = get_node(rstate, id1); - if (!n1) - n1 = new_node(rstate, id1); - n2 = get_node(rstate, id2); - if (!n2) - n2 = new_node(rstate, id2); - - chan->scid = *scid; - chan->nodes[n1idx] = n1; - chan->nodes[!n1idx] = n2; - broadcastable_init(&chan->bcast); - /* This is how we indicate it's not public yet. */ - chan->bcast.timestamp = 0; - chan->sat = satoshis; - - add_chan(n2, chan); - add_chan(n1, chan); - - /* Populate with (inactive) connections */ - init_half_chan(rstate, chan, n1idx); - init_half_chan(rstate, chan, !n1idx); - - uintmap_add(&rstate->chanmap, scid->u64, chan); - - return chan; -} - -/* Verify the signature of a channel_update message */ -static u8 *check_channel_update(const tal_t *ctx, - const struct node_id *node_id, - const secp256k1_ecdsa_signature *node_sig, - const u8 *update) -{ - /* 2 byte msg type + 64 byte signatures */ - int offset = 66; - struct sha256_double hash; - sha256_double(&hash, update + offset, tal_count(update) - offset); - - if (!check_signed_hash_nodeid(&hash, node_sig, node_id)) - return towire_warningfmt(ctx, NULL, - "Bad signature for %s hash %s" - " on channel_update %s", - type_to_string(tmpctx, - secp256k1_ecdsa_signature, - node_sig), - type_to_string(tmpctx, - struct sha256_double, - &hash), - tal_hex(tmpctx, update)); - return NULL; -} - -static u8 *check_channel_announcement(const tal_t *ctx, - const struct node_id *node1_id, const struct node_id *node2_id, - const struct pubkey *bitcoin1_key, const struct pubkey *bitcoin2_key, - const secp256k1_ecdsa_signature *node1_sig, - const secp256k1_ecdsa_signature *node2_sig, - const secp256k1_ecdsa_signature *bitcoin1_sig, - const secp256k1_ecdsa_signature *bitcoin2_sig, const u8 *announcement) -{ - /* 2 byte msg type + 256 byte signatures */ - int offset = 258; - struct sha256_double hash; - sha256_double(&hash, announcement + offset, - tal_count(announcement) - offset); - - if (!check_signed_hash_nodeid(&hash, node1_sig, node1_id)) { - return towire_warningfmt(ctx, NULL, - "Bad node_signature_1 %s hash %s" - " on channel_announcement %s", - type_to_string(tmpctx, - secp256k1_ecdsa_signature, - node1_sig), - type_to_string(tmpctx, - struct sha256_double, - &hash), - tal_hex(tmpctx, announcement)); - } - if (!check_signed_hash_nodeid(&hash, node2_sig, node2_id)) { - return towire_warningfmt(ctx, NULL, - "Bad node_signature_2 %s hash %s" - " on channel_announcement %s", - type_to_string(tmpctx, - secp256k1_ecdsa_signature, - node2_sig), - type_to_string(tmpctx, - struct sha256_double, - &hash), - tal_hex(tmpctx, announcement)); - } - if (!check_signed_hash(&hash, bitcoin1_sig, bitcoin1_key)) { - return towire_warningfmt(ctx, NULL, - "Bad bitcoin_signature_1 %s hash %s" - " on channel_announcement %s", - type_to_string(tmpctx, - secp256k1_ecdsa_signature, - bitcoin1_sig), - type_to_string(tmpctx, - struct sha256_double, - &hash), - tal_hex(tmpctx, announcement)); - } - if (!check_signed_hash(&hash, bitcoin2_sig, bitcoin2_key)) { - return towire_warningfmt(ctx, NULL, - "Bad bitcoin_signature_2 %s hash %s" - " on channel_announcement %s", - type_to_string(tmpctx, - secp256k1_ecdsa_signature, - bitcoin2_sig), - type_to_string(tmpctx, - struct sha256_double, - &hash), - tal_hex(tmpctx, announcement)); - } - return NULL; -} - -/* We allow node announcements for this node if it doesn't otherwise exist, so - * we can process them once it does exist (a channel_announce is being - * validated right now). - * - * If we attach one, remove it on destruction of @ctx. - */ -static void del_pending_node_announcement(const tal_t *ctx UNUSED, - struct pending_node_announce *pna) -{ - if (--pna->refcount == 0) { - pending_node_map_del(pna->rstate->pending_node_map, pna); - tal_free(pna); - } -} - -static void catch_node_announcement(const tal_t *ctx, - struct routing_state *rstate, - struct node_id *nodeid) -{ - struct pending_node_announce *pna; - struct node *node; - - /* No need if we already know about the node. */ - node = get_node(rstate, nodeid); - if (node) - return; - - /* We can have multiple channels announced at same time for nodes; - * but we can only have one of these in the map. */ - pna = pending_node_map_get(rstate->pending_node_map, nodeid); - if (!pna) { - pna = tal(rstate, struct pending_node_announce); - pna->rstate = rstate; - pna->nodeid = *nodeid; - pna->node_announcement = NULL; - pna->timestamp = 0; - pna->index = 0; - pna->refcount = 0; - pna->source_peer = NULL; - pna->spam.node_announcement = NULL; - pna->spam.index = 0; - pending_node_map_add(rstate->pending_node_map, pna); - } - pna->refcount++; - tal_add_destructor2(ctx, del_pending_node_announcement, pna); -} - -static void process_pending_node_announcement(struct routing_state *rstate, - struct node_id *nodeid) -{ - struct pending_node_announce *pna = pending_node_map_get(rstate->pending_node_map, nodeid); - if (!pna) - return; - - if (pna->node_announcement) { - SUPERVERBOSE( - "Processing deferred node_announcement for node %s", - type_to_string(pna, struct node_id, nodeid)); - - /* Can fail it timestamp is now too old */ - if (!routing_add_node_announcement(rstate, - pna->node_announcement, - pna->index, - pna->source_peer, NULL, - false)) - status_unusual("pending node_announcement %s too old?", - tal_hex(tmpctx, pna->node_announcement)); - /* Never send this again. */ - pna->node_announcement = tal_free(pna->node_announcement); - } - if (pna->spam.node_announcement) { - SUPERVERBOSE( - "Processing deferred node_announcement for node %s", - type_to_string(pna, struct node_id, nodeid)); - - /* Can fail it timestamp is now too old */ - if (!routing_add_node_announcement(rstate, - pna->spam.node_announcement, - pna->spam.index, - NULL, NULL, - true)) - status_unusual("pending node_announcement %s too old?", - tal_hex(tmpctx, pna->spam.node_announcement)); - /* Never send this again. */ - pna->spam.node_announcement = tal_free(pna->spam.node_announcement); - } - - /* We don't need to catch any more node_announcements, since we've - * accepted the public channel now. But other pending announcements - * may still hold a reference they use in - * del_pending_node_announcement, so simply delete it from the map. */ - pending_node_map_del(rstate->pending_node_map, notleak(pna)); -} - -static struct pending_cannouncement * -find_pending_cannouncement(struct routing_state *rstate, - const struct short_channel_id *scid) -{ - struct pending_cannouncement *pann; - - pann = pending_cannouncement_map_get(rstate->pending_cannouncements, scid); - - return pann; -} - -static void destroy_pending_cannouncement(struct pending_cannouncement *pending, - struct routing_state *rstate) -{ - pending_cannouncement_map_del(rstate->pending_cannouncements, pending); -} - -static void add_channel_announce_to_broadcast(struct routing_state *rstate, - struct chan *chan, - const u8 *channel_announce, - u32 timestamp, - u32 index) -{ - u8 *addendum = towire_gossip_store_channel_amount(tmpctx, chan->sat); - - chan->bcast.timestamp = timestamp; - /* 0, unless we're loading from store */ - if (index) - chan->bcast.index = index; - else - chan->bcast.index = gossip_store_add(rstate->gs, - channel_announce, - chan->bcast.timestamp, - false, - false, - false, - addendum); -} - -static void delete_chan_messages_from_store(struct routing_state *rstate, - struct chan *chan) -{ - /* If these aren't in the store, these are noops. */ - gossip_store_delete(rstate->gs, - &chan->bcast, WIRE_CHANNEL_ANNOUNCEMENT); - if (chan->half[0].rgraph.index != chan->half[0].bcast.index) - gossip_store_delete(rstate->gs, - &chan->half[0].rgraph, WIRE_CHANNEL_UPDATE); - gossip_store_delete(rstate->gs, - &chan->half[0].bcast, WIRE_CHANNEL_UPDATE); - if (chan->half[1].rgraph.index != chan->half[1].bcast.index) - gossip_store_delete(rstate->gs, - &chan->half[1].rgraph, WIRE_CHANNEL_UPDATE); - gossip_store_delete(rstate->gs, - &chan->half[1].bcast, WIRE_CHANNEL_UPDATE); -} - -static void remove_channel_from_store(struct routing_state *rstate, - struct chan *chan) -{ - /* Put in tombstone marker. Zombie channels will have one already. */ - if (!is_chan_zombie(chan)) - gossip_store_mark_channel_deleted(rstate->gs, &chan->scid); - - /* Now delete old entries. */ - delete_chan_messages_from_store(rstate, chan); -} - -bool routing_add_channel_announcement(struct routing_state *rstate, - const u8 *msg TAKES, - struct amount_sat sat, - u32 index, - const struct node_id *source_peer) -{ - secp256k1_ecdsa_signature node_signature_1, node_signature_2; - secp256k1_ecdsa_signature bitcoin_signature_1, bitcoin_signature_2; - u8 *features; - struct bitcoin_blkid chain_hash; - struct short_channel_id scid; - struct node_id node_id_1; - struct node_id node_id_2; - struct pubkey bitcoin_key_1; - struct pubkey bitcoin_key_2; - struct unupdated_channel *uc; - - /* Make sure we own msg, even if we don't save it. */ - if (taken(msg)) - tal_steal(tmpctx, msg); - - if (!fromwire_channel_announcement( - tmpctx, msg, &node_signature_1, &node_signature_2, - &bitcoin_signature_1, &bitcoin_signature_2, &features, &chain_hash, - &scid, &node_id_1, &node_id_2, &bitcoin_key_1, &bitcoin_key_2)) - return false; - - uc = tal(rstate, struct unupdated_channel); - uc->channel_announce = tal_dup_talarr(uc, u8, msg); - uc->added = gossip_time_now(rstate); - uc->index = index; - uc->sat = sat; - uc->scid = scid; - uc->id[0] = node_id_1; - uc->id[1] = node_id_2; - uc->source_peer = tal_dup_or_null(uc, struct node_id, source_peer); - uintmap_add(&rstate->unupdated_chanmap, scid.u64, uc); - tal_add_destructor2(uc, destroy_unupdated_channel, rstate); - - /* If a node_announcement comes along, save it for once we're updated */ - catch_node_announcement(uc, rstate, &node_id_1); - catch_node_announcement(uc, rstate, &node_id_2); - - return true; -} - -u8 *handle_channel_announcement(struct routing_state *rstate, - const u8 *announce TAKES, - u32 current_blockheight, - const struct short_channel_id **scid, - const struct node_id *source_peer TAKES) -{ - struct pending_cannouncement *pending; - struct bitcoin_blkid chain_hash; - u8 *features, *warn; - secp256k1_ecdsa_signature node_signature_1, node_signature_2; - secp256k1_ecdsa_signature bitcoin_signature_1, bitcoin_signature_2; - struct chan *chan; - - pending = tal(rstate, struct pending_cannouncement); - pending->source_peer = tal_dup_or_null(pending, struct node_id, source_peer); - pending->updates[0] = NULL; - pending->updates[1] = NULL; - pending->update_source_peer[0] = pending->update_source_peer[1] = NULL; - pending->announce = tal_dup_talarr(pending, u8, announce); - pending->update_timestamps[0] = pending->update_timestamps[1] = 0; - - if (!fromwire_channel_announcement(pending, pending->announce, - &node_signature_1, - &node_signature_2, - &bitcoin_signature_1, - &bitcoin_signature_2, - &features, - &chain_hash, - &pending->short_channel_id, - &pending->node_id_1, - &pending->node_id_2, - &pending->bitcoin_key_1, - &pending->bitcoin_key_2)) { - warn = towire_warningfmt(rstate, NULL, - "Malformed channel_announcement %s", - tal_hex(pending, pending->announce)); - goto malformed; - } - - /* We don't use features */ - tal_free(features); - - /* If we know the blockheight, and it's in the future, reject - * out-of-hand. Remember, it should be 6 deep before they tell us - * anyway. */ - if (current_blockheight != 0 - && short_channel_id_blocknum(&pending->short_channel_id) > current_blockheight) { - status_peer_debug(pending->source_peer, - "Ignoring future channel_announcment for %s" - " (current block %u)", - type_to_string(tmpctx, struct short_channel_id, - &pending->short_channel_id), - current_blockheight); - goto ignored; - } - - /* If a prior txout lookup failed there is little point it trying - * again. Just drop the announcement and walk away whistling. */ - if (in_txout_failures(rstate, &pending->short_channel_id)) { - SUPERVERBOSE( - "Ignoring channel_announcement of %s due to a prior txout " - "query failure. The channel was likely closed on-chain.", - type_to_string(tmpctx, struct short_channel_id, - &pending->short_channel_id)); - goto ignored; - } - - /* Check if we know the channel already (no matter in what - * state, we stop here if yes). */ - chan = get_channel(rstate, &pending->short_channel_id); - if (chan != NULL) { - SUPERVERBOSE("%s: %s already has public channel", - __func__, - type_to_string(tmpctx, struct short_channel_id, - &pending->short_channel_id)); - goto ignored; - } - if (get_unupdated_channel(rstate, &pending->short_channel_id)) { - SUPERVERBOSE("%s: %s already has unupdated channel", - __func__, - type_to_string(tmpctx, struct short_channel_id, - &pending->short_channel_id)); - goto ignored; - } - - /* We don't replace previous ones, since we might validate that and - * think this one is OK! */ - if (find_pending_cannouncement(rstate, &pending->short_channel_id)) { - SUPERVERBOSE("%s: %s already has pending cannouncement", - __func__, - type_to_string(tmpctx, struct short_channel_id, - &pending->short_channel_id)); - goto ignored; - } - - /* FIXME: Handle duplicates as per BOLT #7 */ - - /* BOLT #7: - * The receiving node: - *... - * - if the specified `chain_hash` is unknown to the receiver: - * - MUST ignore the message. - */ - if (!bitcoin_blkid_eq(&chain_hash, &chainparams->genesis_blockhash)) { - status_peer_debug(pending->source_peer, - "Received channel_announcement %s for unknown chain %s", - type_to_string(pending, struct short_channel_id, - &pending->short_channel_id), - type_to_string(pending, struct bitcoin_blkid, &chain_hash)); - goto ignored; - } - - /* Note that if node_id_1 or node_id_2 are malformed, it's caught here */ - warn = check_channel_announcement(rstate, - &pending->node_id_1, - &pending->node_id_2, - &pending->bitcoin_key_1, - &pending->bitcoin_key_2, - &node_signature_1, - &node_signature_2, - &bitcoin_signature_1, - &bitcoin_signature_2, - pending->announce); - if (warn) { - /* BOLT #7: - * - * - if `bitcoin_signature_1`, `bitcoin_signature_2`, - * `node_signature_1` OR `node_signature_2` are invalid OR NOT - * correct: - * - SHOULD send a `warning`. - * - MAY close the connection. - * - MUST ignore the message. - */ - goto malformed; - } - - /* Don't add an infinite number of pending announcements. If we're - * catching up with the bitcoin chain, though, they can definitely - * pile up. */ - if (pending_cannouncement_map_count(rstate->pending_cannouncements) - > 100000) { - static bool warned = false; - if (!warned) { - status_peer_unusual(pending->source_peer, - "Flooded by channel_announcements:" - " ignoring some"); - warned = true; - } - goto ignored; - } - - status_peer_debug(pending->source_peer, - "Received channel_announcement for channel %s", - type_to_string(tmpctx, struct short_channel_id, - &pending->short_channel_id)); - - /* Add both endpoints to the pending_node_map so we can stash - * node_announcements while we wait for the txout check */ - catch_node_announcement(pending, rstate, &pending->node_id_1); - catch_node_announcement(pending, rstate, &pending->node_id_2); - - pending_cannouncement_map_add(rstate->pending_cannouncements, pending); - tal_add_destructor2(pending, destroy_pending_cannouncement, rstate); - - /* Success */ - // MSC: Cppcheck 1.86 gets this false positive - // cppcheck-suppress autoVariables - *scid = &pending->short_channel_id; - return NULL; - -malformed: - tal_free(pending); - *scid = NULL; - return warn; - -ignored: - tal_free(pending); - *scid = NULL; - return NULL; -} - -static void process_pending_channel_update(struct daemon *daemon, - struct routing_state *rstate, - const struct short_channel_id *scid, - const u8 *cupdate, - const struct node_id *source_peer) -{ - u8 *err; - - if (!cupdate) - return; - - err = handle_channel_update(rstate, cupdate, source_peer, NULL, false); - if (err) { - /* FIXME: We could send this error back to peer if != NULL */ - status_peer_debug(source_peer, - "Pending channel_update for %s: %s", - type_to_string(tmpctx, struct short_channel_id, - scid), - sanitize_error(tmpctx, err, NULL)); - tal_free(err); - } -} - -bool handle_pending_cannouncement(struct daemon *daemon, - struct routing_state *rstate, - const struct short_channel_id *scid, - struct amount_sat sat, - const u8 *outscript) -{ - const u8 *s; - struct pending_cannouncement *pending; - - pending = find_pending_cannouncement(rstate, scid); - if (!pending) - return false; - - /* BOLT #7: - * - * The receiving node: - *... - * - if the `short_channel_id`'s output... is spent: - * - MUST ignore the message. - */ - if (tal_count(outscript) == 0) { - status_peer_debug(pending->source_peer, - "channel_announcement: no unspent txout %s", - type_to_string(pending, - struct short_channel_id, - scid)); - tal_free(pending); - add_to_txout_failures(rstate, scid); - return false; - } - - /* BOLT #7: - * - * The receiving node: - *... - * - if the `short_channel_id`'s output does NOT correspond to a P2WSH - * (using `bitcoin_key_1` and `bitcoin_key_2`, as specified in - * [BOLT #3](03-transactions.md#funding-transaction-output)) ... - * - MUST ignore the message. - */ - s = scriptpubkey_p2wsh(pending, - bitcoin_redeem_2of2(pending, - &pending->bitcoin_key_1, - &pending->bitcoin_key_2)); - - if (!scripteq(s, outscript)) { - status_peer_debug(pending->source_peer, - "channel_announcement: txout %s expected %s, got %s", - type_to_string( - pending, struct short_channel_id, - scid), - tal_hex(tmpctx, s), - tal_hex(tmpctx, outscript)); - tal_free(pending); - return false; - } - - /* Remove pending now, so below functions don't see it. */ - pending_cannouncement_map_del(rstate->pending_cannouncements, pending); - tal_del_destructor2(pending, destroy_pending_cannouncement, rstate); - - /* Can fail if channel_announcement too old */ - if (!routing_add_channel_announcement(rstate, pending->announce, sat, 0, - pending->source_peer)) - status_peer_unusual(pending->source_peer, - "Could not add channel_announcement %s: too old?", - tal_hex(tmpctx, pending->announce)); - else { - /* Did we have an update waiting? If so, apply now. */ - process_pending_channel_update(daemon, rstate, scid, pending->updates[0], - pending->update_source_peer[0]); - process_pending_channel_update(daemon, rstate, scid, pending->updates[1], - pending->update_source_peer[1]); - } - - tal_free(pending); - return true; -} - -static void update_pending(struct pending_cannouncement *pending, - u32 timestamp, const u8 *update, - const u8 direction, - const struct node_id *source_peer TAKES) -{ - SUPERVERBOSE("Deferring update for pending channel %s/%d", - type_to_string(tmpctx, struct short_channel_id, - &pending->short_channel_id), direction); - - if (pending->update_timestamps[direction] < timestamp) { - if (pending->updates[direction]) { - status_peer_debug(source_peer, - "Replacing existing update"); - tal_free(pending->updates[direction]); - } - pending->updates[direction] - = tal_dup_talarr(pending, u8, update); - pending->update_timestamps[direction] = timestamp; - tal_free(pending->update_source_peer[direction]); - pending->update_source_peer[direction] - = tal_dup_or_null(pending, struct node_id, source_peer); - } else { - /* Don't leak if we don't update! */ - if (taken(source_peer)) - tal_free(source_peer); - } -} - -static void delete_spam_update(struct routing_state *rstate, - struct half_chan *hc) -{ - /* Spam updates will have a unique rgraph index */ - if (hc->rgraph.index == hc->bcast.index) - return; - gossip_store_delete(rstate->gs, &hc->rgraph, - WIRE_CHANNEL_UPDATE); - hc->rgraph.index = hc->bcast.index; - hc->rgraph.timestamp = hc->bcast.timestamp; -} - -static bool is_chan_dying(struct routing_state *rstate, - const struct short_channel_id *scid) -{ - for (size_t i = 0; i < tal_count(rstate->dying_channels); i++) { - if (short_channel_id_eq(&rstate->dying_channels[i].scid, scid)) - return true; - } - return false; -} - -void tell_lightningd_peer_update(struct routing_state *rstate, - const struct node_id *source_peer, - struct short_channel_id scid, - u32 fee_base_msat, - u32 fee_ppm, - u16 cltv_delta, - struct amount_msat htlc_minimum, - struct amount_msat htlc_maximum) -{ - struct peer_update remote_update; - u8* msg; - remote_update.scid = scid; - remote_update.fee_base = fee_base_msat; - remote_update.fee_ppm = fee_ppm; - remote_update.cltv_delta = cltv_delta; - remote_update.htlc_minimum_msat = htlc_minimum; - remote_update.htlc_maximum_msat = htlc_maximum; - msg = towire_gossipd_remote_channel_update(NULL, source_peer, &remote_update); - daemon_conn_send(rstate->daemon->master, take(msg)); -} - -/* Is this channel_update different from prev (not sigs and timestamps)? */ -static bool cupdate_different(struct gossip_store *gs, - const struct half_chan *hc, - const u8 *cupdate) -{ - const u8 *oparts[2], *nparts[2]; - size_t osizes[2], nsizes[2]; - const u8 *orig; - - /* Get last one we have. */ - orig = gossip_store_get(tmpctx, gs, hc->bcast.index); - get_cupdate_parts(orig, oparts, osizes); - get_cupdate_parts(cupdate, nparts, nsizes); - - return !memeq(oparts[0], osizes[0], nparts[0], nsizes[0]) - || !memeq(oparts[1], osizes[1], nparts[1], nsizes[1]); -} - -bool routing_add_channel_update(struct routing_state *rstate, - const u8 *update TAKES, - u32 index, - /* NULL if it's us */ - const struct node_id *source_peer, - bool ignore_timestamp, - bool force_spam_flag, - bool force_zombie_flag) -{ - secp256k1_ecdsa_signature signature; - struct short_channel_id short_channel_id; - u32 timestamp; - u8 message_flags, channel_flags; - u16 expiry; - struct amount_msat htlc_minimum, htlc_maximum; - u32 fee_base_msat; - u32 fee_proportional_millionths; - struct bitcoin_blkid chain_hash; - struct chan *chan; - struct half_chan *hc; - struct unupdated_channel *uc; - u8 direction; - struct amount_sat sat; - bool spam; - bool zombie; - bool dying; - - /* Make sure we own msg, even if we don't save it. */ - if (taken(update)) - tal_steal(tmpctx, update); - - if (!fromwire_channel_update( - update, &signature, &chain_hash, - &short_channel_id, ×tamp, - &message_flags, &channel_flags, - &expiry, &htlc_minimum, &fee_base_msat, - &fee_proportional_millionths, - &htlc_maximum)) - return false; - - direction = channel_flags & 0x1; - chan = get_channel(rstate, &short_channel_id); - - if (chan) { - uc = NULL; - sat = chan->sat; - zombie = is_chan_zombie(chan); - dying = is_chan_dying(rstate, &short_channel_id); - } else { - /* Maybe announcement was waiting for this update? */ - uc = get_unupdated_channel(rstate, &short_channel_id); - if (!uc) { - if (index) - return false; - /* Allow ld to process a private channel update */ - tell_lightningd_peer_update(rstate, source_peer, - short_channel_id, fee_base_msat, - fee_proportional_millionths, - expiry, htlc_minimum, - htlc_maximum); - return false; - } - sat = uc->sat; - /* When loading zombies from the store. */ - zombie = force_zombie_flag; - dying = false; - } - - /* Reject update if the `htlc_maximum_msat` is greater - * than the total available channel satoshis */ - if (amount_msat_greater_sat(htlc_maximum, sat)) - return false; - - /* Check timestamp is sane (unless from store). */ - if (!index && !timestamp_reasonable(rstate, timestamp)) { - SUPERVERBOSE("Ignoring update timestamp %u for %s/%u", - timestamp, - type_to_string(tmpctx, struct short_channel_id, - &short_channel_id), - direction); - return false; - } - - /* OK, we're going to accept this, so create chan if doesn't exist */ - if (uc) { - assert(!chan); - chan = new_chan(rstate, &short_channel_id, - &uc->id[0], &uc->id[1], sat); - /* Assign zombie flag if loading zombie from store */ - if (force_zombie_flag) - chan->half[direction].zombie = true; - } - - /* Discard older updates */ - hc = &chan->half[direction]; - - if (is_halfchan_defined(hc) && !ignore_timestamp) { - /* The gossip_store should contain a single broadcastable entry - * and potentially one rate-limited entry. Any more is a bug */ - if (index){ - if (!force_spam_flag){ - status_broken("gossip_store broadcastable " - "channel_update %u replaces %u!", - index, hc->bcast.index); - return false; - } else if (hc->bcast.index != hc->rgraph.index){ - status_broken("gossip_store rate-limited " - "channel_update %u replaces %u!", - index, hc->rgraph.index); - return false; - } - } - - if (timestamp <= hc->rgraph.timestamp) { - SUPERVERBOSE("Ignoring outdated update."); - /* Ignoring != failing */ - return true; - } - - /* Allow redundant updates once every 7 days */ - if (timestamp < hc->bcast.timestamp + GOSSIP_PRUNE_INTERVAL(rstate->dev_fast_gossip_prune) / 2 - && !cupdate_different(rstate->gs, hc, update)) { - SUPERVERBOSE("Ignoring redundant update for %s/%u" - " (last %u, now %u)", - type_to_string(tmpctx, - struct short_channel_id, - &short_channel_id), - direction, hc->bcast.timestamp, timestamp); - /* Ignoring != failing */ - return true; - } - - /* Make sure it's not spamming us */ - if (!local_direction(rstate, chan, NULL) - && !ratelimit(rstate, - &hc->tokens, hc->bcast.timestamp, timestamp)) { - status_peer_debug(source_peer, - "Spammy update for %s/%u flagged" - " (last %u, now %u)", - type_to_string(tmpctx, - struct short_channel_id, - &short_channel_id), - direction, - hc->bcast.timestamp, timestamp); - spam = true; - } else { - spam = false; - } - } else { - spam = false; - } - if (force_spam_flag) - spam = true; - - /* Delete any prior entries (noop if they don't exist) */ - delete_spam_update(rstate, hc); - if (!spam) - gossip_store_delete(rstate->gs, &hc->bcast, - WIRE_CHANNEL_UPDATE); - - /* Update timestamp(s) */ - hc->rgraph.timestamp = timestamp; - if (!spam) - hc->bcast.timestamp = timestamp; - - /* If this is a peer's update to one of our local channels, tell lightningd. */ - if (node_id_eq(&chan->nodes[!direction]->id, &rstate->daemon->id)) { - /* give lightningd the channel's inbound info to store to db */ - tell_lightningd_peer_update(rstate, - /* Note: we can get public - * channel_updates from other than - * direct peer! So tell lightningd - * to trust us. */ - NULL, - short_channel_id, fee_base_msat, - fee_proportional_millionths, - expiry, htlc_minimum, - htlc_maximum); - } - - /* BOLT #7: - * - MUST consider the `timestamp` of the `channel_announcement` to be - * the `timestamp` of a corresponding `channel_update`. - * - MUST consider whether to send the `channel_announcement` after - * receiving the first corresponding `channel_update`. - */ - if (uc) { - add_channel_announce_to_broadcast(rstate, chan, - uc->channel_announce, - timestamp, - uc->index); - } - - /* Handle resurrection of zombie channels if the other side of the - * zombie channel has a recent timestamp. */ - if (zombie && timestamp_reasonable(rstate, - chan->half[!direction].bcast.timestamp) && - chan->half[!direction].bcast.index && !index) { - status_peer_debug(source_peer, - "Resurrecting zombie channel %s.", - type_to_string(tmpctx, - struct short_channel_id, - &chan->scid)); - const u8 *zombie_announcement = NULL; - const u8 *zombie_addendum = NULL; - const u8 *zombie_update[2] = {NULL, NULL}; - /* Resurrection is a careful process. First delete the zombie- - * flagged channel_announcement which has already been - * tombstoned, and re-add to the store without zombie flag. */ - zombie_announcement = gossip_store_get(tmpctx, rstate->gs, - chan->bcast.index); - u32 offset = tal_count(zombie_announcement) + - sizeof(struct gossip_hdr); - /* The channel_announcement addendum reminds us of its size. */ - zombie_addendum = gossip_store_get(tmpctx, rstate->gs, - chan->bcast.index + offset); - gossip_store_delete(rstate->gs, &chan->bcast, - WIRE_CHANNEL_ANNOUNCEMENT); - chan->bcast.index = - gossip_store_add(rstate->gs, zombie_announcement, - chan->bcast.timestamp, - false, false, false, zombie_addendum); - /* Deletion of the old addendum is optional. */ - /* This opposing channel_update has been stashed away. Now that - * there are two valid updates, this one gets restored. */ - /* FIXME: Handle spam case probably needs a helper f'n */ - zombie_update[0] = gossip_store_get(tmpctx, rstate->gs, - chan->half[!direction].bcast.index); - if (chan->half[!direction].bcast.index != chan->half[!direction].rgraph.index) { - /* Don't forget the spam channel_update */ - zombie_update[1] = gossip_store_get(tmpctx, rstate->gs, - chan->half[!direction].rgraph.index); - gossip_store_delete(rstate->gs, &chan->half[!direction].rgraph, - WIRE_CHANNEL_UPDATE); - } - gossip_store_delete(rstate->gs, &chan->half[!direction].bcast, - WIRE_CHANNEL_UPDATE); - chan->half[!direction].bcast.index = - gossip_store_add(rstate->gs, zombie_update[0], - chan->half[!direction].bcast.timestamp, - false, false, false, NULL); - if (zombie_update[1]) - chan->half[!direction].rgraph.index = - gossip_store_add(rstate->gs, zombie_update[1], - chan->half[!direction].rgraph.timestamp, - false, true, false, NULL); - else - chan->half[!direction].rgraph.index = chan->half[!direction].bcast.index; - - /* It's a miracle! */ - chan->half[0].zombie = false; - chan->half[1].zombie = false; - zombie = false; - } - - /* If we're loading from store, this means we don't re-add to store. */ - if (index) { - if (!spam) - hc->bcast.index = index; - hc->rgraph.index = index; - } else { - hc->rgraph.index - = gossip_store_add(rstate->gs, update, timestamp, - zombie, spam, dying, NULL); - if (hc->bcast.timestamp > rstate->last_timestamp - && hc->bcast.timestamp < time_now().ts.tv_sec) - rstate->last_timestamp = hc->bcast.timestamp; - if (!spam) - hc->bcast.index = hc->rgraph.index; - - peer_supplied_good_gossip(rstate->daemon, source_peer, 1); - } - - if (uc) { - /* If we were waiting for these nodes to appear (or gain a - public channel), process node_announcements now */ - process_pending_node_announcement(rstate, &chan->nodes[0]->id); - process_pending_node_announcement(rstate, &chan->nodes[1]->id); - tal_free(uc); - } - - status_peer_debug(source_peer, - "Received %schannel_update for %schannel %s/%d now %s", - ignore_timestamp ? "(forced) " : "", - dying ? "dying ": "", - type_to_string(tmpctx, struct short_channel_id, - &short_channel_id), - channel_flags & 0x01, - channel_flags & ROUTING_FLAGS_DISABLED ? "DISABLED" : "ACTIVE"); - - return true; -} - -bool would_ratelimit_cupdate(struct routing_state *rstate, - const struct half_chan *hc, - u32 timestamp) -{ - return update_tokens(rstate, hc->tokens, hc->bcast.timestamp, timestamp) - >= TOKENS_PER_MSG; -} - -static const struct node_id *get_channel_owner(struct routing_state *rstate, - const struct short_channel_id *scid, - int direction) -{ - struct chan *chan = get_channel(rstate, scid); - struct unupdated_channel *uc; - - if (chan) - return &chan->nodes[direction]->id; - - /* Might be unupdated channel */ - uc = get_unupdated_channel(rstate, scid); - if (uc) - return &uc->id[direction]; - return NULL; -} - -u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, - const struct node_id *source_peer, - struct short_channel_id *unknown_scid, - bool force) -{ - u8 *serialized; - const struct node_id *owner; - secp256k1_ecdsa_signature signature; - struct short_channel_id short_channel_id; - u32 timestamp; - u8 message_flags, channel_flags; - u16 expiry; - struct amount_msat htlc_minimum, htlc_maximum; - u32 fee_base_msat; - u32 fee_proportional_millionths; - struct bitcoin_blkid chain_hash; - u8 direction; - struct pending_cannouncement *pending; - u8 *warn; - - serialized = tal_dup_talarr(tmpctx, u8, update); - if (!fromwire_channel_update(serialized, &signature, - &chain_hash, &short_channel_id, - ×tamp, &message_flags, - &channel_flags, &expiry, - &htlc_minimum, &fee_base_msat, - &fee_proportional_millionths, - &htlc_maximum)) { - /* FIXME: We removed a warning about the - * channel_update being malformed since the warning - * could cause lnd to disconnect (seems they treat - * channel-unrelated warnings as fatal?). This was - * caused by lnd not enforcing the `htlc_maximum`, - * thus the parsing would fail. We can re-add the - * warning once our assumption that `htlc_maximum` - * being set is valid. */ - return NULL; - } - direction = channel_flags & 0x1; - - /* BOLT #7: - * - * The receiving node: - *... - * - if the specified `chain_hash` value is unknown (meaning it isn't - * active on the specified chain): - * - MUST ignore the channel update. - */ - if (!bitcoin_blkid_eq(&chain_hash, &chainparams->genesis_blockhash)) { - status_peer_debug(source_peer, - "Received channel_update for unknown chain %s", - type_to_string(tmpctx, struct bitcoin_blkid, - &chain_hash)); - return NULL; - } - - /* If we dropped the matching announcement for this channel due to the - * txout query failing, don't report failure, it's just too noisy on - * mainnet */ - if (in_txout_failures(rstate, &short_channel_id)) - return NULL; - - /* If we have an unvalidated channel, just queue on that */ - pending = find_pending_cannouncement(rstate, &short_channel_id); - if (pending) { - status_peer_debug(source_peer, - "Updated pending announce with update %s/%u", - type_to_string(tmpctx, - struct short_channel_id, - &short_channel_id), - direction); - update_pending(pending, timestamp, serialized, direction, source_peer); - return NULL; - } - - owner = get_channel_owner(rstate, &short_channel_id, direction); - if (!owner) { - /* This may be a local channel we don't know about. If it's from a peer, - * check signature assuming it's from that peer, and if it's valid, hand to ld */ - if (source_peer - && check_channel_update(tmpctx, source_peer, &signature, serialized) == NULL) { - tell_lightningd_peer_update(rstate, source_peer, - short_channel_id, fee_base_msat, - fee_proportional_millionths, - expiry, htlc_minimum, - htlc_maximum); - return NULL; - } - - if (unknown_scid) - *unknown_scid = short_channel_id; - bad_gossip_order(serialized, - source_peer, - tal_fmt(tmpctx, "%s/%u", - type_to_string(tmpctx, - struct short_channel_id, - &short_channel_id), - direction)); - return NULL; - } - - warn = check_channel_update(rstate, owner, &signature, serialized); - if (warn) { - /* BOLT #7: - * - * - if `signature` is not a valid signature, using `node_id` - * of the double-SHA256 of the entire message following the - * `signature` field (including unknown fields following - * `fee_proportional_millionths`): - * - SHOULD send a `warning` and close the connection. - * - MUST NOT process the message further. - */ - return warn; - } - - routing_add_channel_update(rstate, take(serialized), 0, source_peer, force, - false, false); - return NULL; -} - -/* Get non-signature, non-timestamp parts of (valid!) node_announcement, - * with TLV broken out separately */ -static void get_nannounce_parts(const u8 *node_announcement, - const u8 *parts[3], - size_t sizes[3]) -{ - size_t len, ad_len; - const u8 *flen, *ad_start; - - /* BOLT #7: - * - * 1. type: 257 (`node_announcement`) - * 2. data: - * * [`signature`:`signature`] - * * [`u16`:`flen`] - * * [`flen*byte`:`features`] - * * [`u32`:`timestamp`] - *... - */ - /* Note: 2 bytes for `type` field */ - /* We already checked it's valid before accepting */ - assert(tal_count(node_announcement) > 2 + 64); - parts[0] = node_announcement + 2 + 64; - - /* Read flen to get size */ - flen = parts[0]; - len = tal_count(node_announcement) - (2 + 64); - sizes[0] = 2 + fromwire_u16(&flen, &len); - assert(flen != NULL && len >= 4); - - /* BOLT-0fe3485a5320efaa2be8cfa0e570ad4d0259cec3 #7: - * - * * [`u32`:`timestamp`] - * * [`point`:`node_id`] - * * [`3*byte`:`rgb_color`] - * * [`32*byte`:`alias`] - * * [`u16`:`addrlen`] - * * [`addrlen*byte`:`addresses`] - * * [`node_ann_tlvs`:`tlvs`] - */ - parts[1] = node_announcement + 2 + 64 + sizes[0] + 4; - - /* Find the end of the addresses */ - ad_start = parts[1] + 33 + 3 + 32; - len = tal_count(node_announcement) - - (2 + 64 + sizes[0] + 4 + 33 + 3 + 32); - ad_len = fromwire_u16(&ad_start, &len); - assert(ad_start != NULL && len >= ad_len); - - sizes[1] = 33 + 3 + 32 + 2 + ad_len; - - /* Is there a TLV ? */ - sizes[2] = len - ad_len; - if (sizes[2] != 0) - parts[2] = parts[1] + sizes[1]; - else - parts[2] = NULL; -} - -/* Is this node_announcement different from prev (not sigs and timestamps)? */ -static bool nannounce_different(struct gossip_store *gs, - const struct node *node, - const u8 *nannounce) -{ - const u8 *oparts[3], *nparts[3]; - size_t osizes[3], nsizes[3]; - const u8 *orig; - - /* Get last one we have. */ - orig = gossip_store_get(tmpctx, gs, node->bcast.index); - get_nannounce_parts(orig, oparts, osizes); - get_nannounce_parts(nannounce, nparts, nsizes); - - return !memeq(oparts[0], osizes[0], nparts[0], nsizes[0]) - || !memeq(oparts[1], osizes[1], nparts[1], nsizes[1]) - || !memeq(oparts[2], osizes[2], nparts[2], nsizes[2]); -} - -bool routing_add_node_announcement(struct routing_state *rstate, - const u8 *msg TAKES, - u32 index, - const struct node_id *source_peer TAKES, - bool *was_unknown, - bool force_spam_flag) -{ - struct node *node; - secp256k1_ecdsa_signature signature; - u32 timestamp; - struct node_id node_id; - u8 rgb_color[3]; - u8 alias[32]; - u8 *features, *addresses; - struct tlv_node_ann_tlvs *na_tlv; - bool spam; - - if (was_unknown) - *was_unknown = false; - - /* Make sure we own msg, even if we don't save it. */ - if (taken(msg)) - tal_steal(tmpctx, msg); - - /* Note: validity of node_id is already checked. */ - if (!fromwire_node_announcement(tmpctx, msg, - &signature, &features, ×tamp, - &node_id, rgb_color, alias, - &addresses, - &na_tlv)) { - return false; - } - - node = get_node(rstate, &node_id); - - if (node == NULL || !node_has_broadcastable_channels(node)) { - struct pending_node_announce *pna; - /* BOLT #7: - * - * - if `node_id` is NOT previously known from a - * `channel_announcement` message, OR if `timestamp` is NOT - * greater than the last-received `node_announcement` from - * this `node_id`: - * - SHOULD ignore the message. - */ - /* Check if we are currently verifying the txout for a - * matching channel */ - pna = pending_node_map_get(rstate->pending_node_map, - &node_id); - if (!pna) { - if (was_unknown) - *was_unknown = true; - /* Don't complain if it's a zombie node! */ - if (!node || !is_node_zombie(node)) { - bad_gossip_order(msg, source_peer, - type_to_string(tmpctx, struct node_id, - &node_id)); - } - return false; - } else if (timestamp <= pna->timestamp) - /* Ignore old ones: they're OK (unless from store). */ - return index == 0; - - SUPERVERBOSE("Deferring node_announcement for node %s", - type_to_string(tmpctx, struct node_id, &node_id)); - /* a pending spam node announcement is possible when loading - * from the store */ - if (index && force_spam_flag) { - tal_free(pna->spam.node_announcement); - pna->spam.node_announcement = tal_dup_talarr(pna, u8, msg); - pna->spam.index = index; - } else { - tal_free(pna->node_announcement); - tal_free(pna->source_peer); - pna->node_announcement = tal_dup_talarr(pna, u8, msg); - pna->source_peer = tal_dup_or_null(pna, struct node_id, source_peer); - pna->timestamp = timestamp; - pna->index = index; - } - return true; - } - - if (node->bcast.index) { - u32 redundant_time; - - /* The gossip_store should contain a single broadcastable entry - * and potentially one rate-limited entry. Any more is a bug */ - if (index){ - if (!force_spam_flag){ - status_broken("gossip_store broadcastable " - "node_announcement %u replaces %u!", - index, node->bcast.index); - return false; - } else if (node->bcast.index != node->rgraph.index){ - status_broken("gossip_store rate-limited " - "node_announcement %u replaces %u!", - index, node->rgraph.index); - return false; - } - } - - if (node->rgraph.timestamp >= timestamp) { - SUPERVERBOSE("Ignoring node announcement, it's outdated."); - /* OK unless we're loading from store */ - return index == 0; - } - - /* Allow redundant updates once a day (faster in dev-fast-gossip-prune mode) */ - redundant_time = GOSSIP_PRUNE_INTERVAL(rstate->dev_fast_gossip_prune) / 14; - if (timestamp < node->bcast.timestamp + redundant_time - && !nannounce_different(rstate->gs, node, msg)) { - SUPERVERBOSE( - "Ignoring redundant nannounce for %s" - " (last %u, now %u)", - type_to_string(tmpctx, struct node_id, &node_id), - node->bcast.timestamp, timestamp); - /* Ignoring != failing */ - return true; - } - - /* Make sure it's not spamming us. */ - if (!ratelimit(rstate, - &node->tokens, node->bcast.timestamp, timestamp)) { - status_peer_debug(source_peer, - "Spammy nannounce for %s flagged" - " (last %u, now %u)", - type_to_string(tmpctx, - struct node_id, - &node_id), - node->bcast.timestamp, timestamp); - spam = true; - } else { - spam = false; - } - } else { - spam = false; - } - if (force_spam_flag) - spam = true; - - /* Routing graph always references the latest message. */ - node->rgraph.timestamp = timestamp; - if (!spam) { - node->bcast.timestamp = timestamp; - /* remove prior spam update if one exists */ - if (node->rgraph.index != node->bcast.index) { - gossip_store_delete(rstate->gs, &node->rgraph, - WIRE_NODE_ANNOUNCEMENT); - } - /* Harmless if it was never added */ - gossip_store_delete(rstate->gs, &node->bcast, - WIRE_NODE_ANNOUNCEMENT); - /* Remove prior spam update. */ - } else if (node->rgraph.index != node->bcast.index) { - gossip_store_delete(rstate->gs, &node->rgraph, - WIRE_NODE_ANNOUNCEMENT); - } - - /* Don't add to the store if it was loaded from the store. */ - if (index) { - node->rgraph.index = index; - if (!spam) - node->bcast.index = index; - } else { - node->rgraph.index - = gossip_store_add(rstate->gs, msg, timestamp, - false, spam, false, NULL); - if (node->bcast.timestamp > rstate->last_timestamp - && node->bcast.timestamp < time_now().ts.tv_sec) - rstate->last_timestamp = node->bcast.timestamp; - if (!spam) - node->bcast.index = node->rgraph.index; - - peer_supplied_good_gossip(rstate->daemon, source_peer, 1); - } - - /* Only log this if *not* loading from store. */ - if (!index) - status_peer_debug(source_peer, - "Received node_announcement for node %s", - type_to_string(tmpctx, struct node_id, - &node_id)); - - return true; -} - -u8 *handle_node_announcement(struct routing_state *rstate, const u8 *node_ann, - const struct node_id *source_peer TAKES, - bool *was_unknown) -{ - u8 *serialized; - struct sha256_double hash; - secp256k1_ecdsa_signature signature; - u32 timestamp; - struct node_id node_id; - u8 rgb_color[3]; - u8 alias[32]; - u8 *features, *addresses; - struct wireaddr *wireaddrs; - size_t len = tal_count(node_ann); - struct tlv_node_ann_tlvs *na_tlv; - - if (was_unknown) - *was_unknown = false; - - serialized = tal_dup_arr(tmpctx, u8, node_ann, len, 0); - if (!fromwire_node_announcement(tmpctx, serialized, - &signature, &features, ×tamp, - &node_id, rgb_color, alias, - &addresses, - &na_tlv)) { - /* BOLT #7: - * - * - if `node_id` is NOT a valid compressed public key: - * - SHOULD send a `warning`. - * - MAY close the connection. - * - MUST NOT process the message further. - */ - /* FIXME: We removed a warning about the - * node_announcement being malformed since the warning - * could cause lnd to disconnect (seems they treat - * channel-unrelated warnings as fatal?). - */ - return NULL; - } - - sha256_double(&hash, serialized + 66, tal_count(serialized) - 66); - /* If node_id is invalid, it fails here */ - if (!check_signed_hash_nodeid(&hash, &signature, &node_id)) { - /* BOLT #7: - * - * - if `signature` is not a valid signature, using - * `node_id` of the double-SHA256 of the entire - * message following the `signature` field - * (including unknown fields following - * `fee_proportional_millionths`): - * - SHOULD send a `warning` and close the connection. - * - MUST NOT process the message further. - */ - u8 *warn = towire_warningfmt(rstate, NULL, - "Bad signature for %s hash %s" - " on node_announcement %s", - type_to_string(tmpctx, - secp256k1_ecdsa_signature, - &signature), - type_to_string(tmpctx, - struct sha256_double, - &hash), - tal_hex(tmpctx, node_ann)); - return warn; - } - - wireaddrs = fromwire_wireaddr_array(tmpctx, addresses); - if (!wireaddrs) { - /* BOLT #7: - * - * - if `addrlen` is insufficient to hold the address - * descriptors of the known types: - * - SHOULD send a `warning`. - * - MAY close the connection. - */ - u8 *warn = towire_warningfmt(rstate, NULL, - "Malformed wireaddrs %s in %s.", - tal_hex(tmpctx, wireaddrs), - tal_hex(tmpctx, node_ann)); - return warn; - } - - /* May still fail, if we don't know the node. */ - routing_add_node_announcement(rstate, serialized, 0, source_peer, was_unknown, false); - return NULL; -} - -void route_prune(struct routing_state *rstate) -{ - u64 now = gossip_time_now(rstate).ts.tv_sec; - /* Anything below this highwater mark ought to be pruned */ - const s64 highwater = now - GOSSIP_PRUNE_INTERVAL(rstate->dev_fast_gossip_prune); - struct chan **pruned = tal_arr(tmpctx, struct chan *, 0); - u64 idx; - - /* Now iterate through all channels and see if it is still alive */ - for (struct chan *chan = uintmap_first(&rstate->chanmap, &idx); - chan; - chan = uintmap_after(&rstate->chanmap, &idx)) { - /* These have been pruned already */ - if (is_chan_zombie(chan)) - continue; - - /* BOLT #7: - * - if the `timestamp` of the latest `channel_update` in - * either direction is older than two weeks (1209600 seconds): - * - MAY prune the channel. - */ - /* This is a fancy way of saying "both ends must refresh!" */ - if (!is_halfchan_defined(&chan->half[0]) - || chan->half[0].bcast.timestamp < highwater - || !is_halfchan_defined(&chan->half[1]) - || chan->half[1].bcast.timestamp < highwater) { - if (local_direction(rstate, chan, NULL)) - status_unusual("Pruning local channel %s from gossip_store: not refreshed in over two weeks", - type_to_string(tmpctx, struct short_channel_id, - &chan->scid)); - - status_debug( - "Pruning channel %s from network view (ages %"PRIu64" and %"PRIu64"s)", - type_to_string(tmpctx, struct short_channel_id, - &chan->scid), - is_halfchan_defined(&chan->half[0]) - ? now - chan->half[0].bcast.timestamp : 0, - is_halfchan_defined(&chan->half[1]) - ? now - chan->half[1].bcast.timestamp : 0); - - /* This may perturb iteration so do outside loop. */ - tal_arr_expand(&pruned, chan); - } - } - - /* Look for channels we had an announcement for, but no update. */ - for (struct unupdated_channel *uc - = uintmap_first(&rstate->unupdated_chanmap, &idx); - uc; - uc = uintmap_after(&rstate->unupdated_chanmap, &idx)) { - if (uc->added.ts.tv_sec < highwater) { - tal_free(uc); - } - } - - /* Now free all the chans and maybe even nodes. */ - for (size_t i = 0; i < tal_count(pruned); i++) { - remove_channel_from_store(rstate, pruned[i]); - free_chan(rstate, pruned[i]); - } -} - -struct timeabs gossip_time_now(const struct routing_state *rstate) -{ - if (rstate->dev_gossip_time) - return *rstate->dev_gossip_time; - - return time_now(); -} - -const char *unfinalized_entries(const tal_t *ctx, struct routing_state *rstate) -{ - struct unupdated_channel *uc; - u64 index; - struct pending_node_announce *pna; - struct pending_node_map_iter it; - - uc = uintmap_first(&rstate->unupdated_chanmap, &index); - if (uc) - return tal_fmt(ctx, "Unupdated channel_announcement at %u", - uc->index); - - pna = pending_node_map_first(rstate->pending_node_map, &it); - if (pna) - return tal_fmt(ctx, "Waiting node_announcement at %u", - pna->index); - - return NULL; -} - -/* Gossip store was corrupt, forget anything we loaded. */ -void remove_all_gossip(struct routing_state *rstate) -{ - struct node *n; - struct node_map_iter nit; - struct chan *c; - struct unupdated_channel *uc; - u64 index; - struct pending_cannouncement *pca; - struct pending_cannouncement_map_iter pit; - struct pending_node_map_iter pnait; - - /* We don't want them to try to delete from store, so do this - * manually. */ - while ((n = node_map_first(rstate->nodes, &nit)) != NULL) { - tal_del_destructor2(n, destroy_node, rstate); - node_map_del(rstate->nodes, n); - tal_free(n); - } - - /* Now free all the channels. */ - while ((c = uintmap_first(&rstate->chanmap, &index)) != NULL) { - uintmap_del(&rstate->chanmap, index); - if (rstate->daemon->developer) - c->sat = amount_sat((unsigned long)c); - tal_free(c); - } - - while ((uc = uintmap_first(&rstate->unupdated_chanmap, &index)) != NULL) - tal_free(uc); - - while ((pca = pending_cannouncement_map_first(rstate->pending_cannouncements, &pit)) != NULL) - tal_free(pca); - - /* Freeing unupdated chanmaps should empty this */ - assert(pending_node_map_first(rstate->pending_node_map, &pnait) == NULL); -} - -static void channel_spent(struct routing_state *rstate, - struct chan *chan STEALS) -{ - status_debug("Deleting channel %s due to the funding outpoint being " - "spent", - type_to_string(tmpctx, struct short_channel_id, - &chan->scid)); - /* Suppress any now-obsolete updates/announcements */ - add_to_txout_failures(rstate, &chan->scid); - remove_channel_from_store(rstate, chan); - /* Freeing is sufficient since everything else is allocated off - * of the channel and this takes care of unregistering - * the channel */ - free_chan(rstate, chan); -} - -void routing_expire_channels(struct routing_state *rstate, u32 blockheight) -{ - struct chan *chan; - - for (size_t i = 0; i < tal_count(rstate->dying_channels); i++) { - struct dying_channel *d = rstate->dying_channels + i; - - if (blockheight < d->deadline_blockheight) - continue; - chan = get_channel(rstate, &d->scid); - if (chan) - channel_spent(rstate, chan); - /* Delete dying marker itself */ - gossip_store_delete(rstate->gs, - &d->marker, WIRE_GOSSIP_STORE_CHAN_DYING); - tal_arr_remove(&rstate->dying_channels, i); - i--; - } -} - -void remember_chan_dying(struct routing_state *rstate, - const struct short_channel_id *scid, - u32 deadline_blockheight, - u64 index) -{ - struct dying_channel d; - d.scid = *scid; - d.deadline_blockheight = deadline_blockheight; - d.marker.index = index; - tal_arr_expand(&rstate->dying_channels, d); -} - -void routing_channel_spent(struct routing_state *rstate, - u32 current_blockheight, - struct chan *chan) -{ - u64 index; - u32 deadline; - u8 *msg; - - /* FIXME: We should note that delay is not necessary (or even - * sensible) for local channels! */ - if (local_direction(rstate, chan, NULL)) { - channel_spent(rstate, chan); - return; - } - - /* BOLT #7: - * - once its funding output has been spent OR reorganized out: - * - SHOULD forget a channel after a 12-block delay. - */ - deadline = current_blockheight + 12; - - /* Save to gossip_store in case we restart */ - msg = towire_gossip_store_chan_dying(tmpctx, &chan->scid, deadline); - index = gossip_store_add(rstate->gs, msg, 0, false, false, false, NULL); - - /* Mark it dying, so we don't gossip it */ - gossip_store_mark_dying(rstate->gs, &chan->bcast, - WIRE_CHANNEL_ANNOUNCEMENT); - for (int dir = 0; dir < ARRAY_SIZE(chan->half); dir++) { - if (is_halfchan_defined(&chan->half[dir])) { - gossip_store_mark_dying(rstate->gs, - &chan->half[dir].bcast, - WIRE_CHANNEL_UPDATE); - } - } - - /* Remember locally so we can kill it in 12 blocks */ - status_debug("channel %s closing soon due" - " to the funding outpoint being spent", - type_to_string(msg, struct short_channel_id, &chan->scid)); - remember_chan_dying(rstate, &chan->scid, deadline, index); -} diff --git a/gossipd/routing.h b/gossipd/routing.h deleted file mode 100644 index 71062b65837f..000000000000 --- a/gossipd/routing.h +++ /dev/null @@ -1,411 +0,0 @@ -#ifndef LIGHTNING_GOSSIPD_ROUTING_H -#define LIGHTNING_GOSSIPD_ROUTING_H -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct daemon; -struct peer; -struct routing_state; - -struct half_chan { - /* Timestamp and index into store file - safe to broadcast */ - struct broadcastable bcast; - - /* Most recent gossip for the routing graph - may be rate-limited and - * non-broadcastable. If there is no spam, rgraph == bcast. */ - struct broadcastable rgraph; - - /* Token bucket */ - u8 tokens; - - /* Disabled channel waiting for a channel_update from both sides. */ - bool zombie; -}; - -struct chan { - struct short_channel_id scid; - - /* - * half[0]->src == nodes[0] half[0]->dst == nodes[1] - * half[1]->src == nodes[1] half[1]->dst == nodes[0] - */ - struct half_chan half[2]; - /* node[0].id < node[1].id */ - struct node *nodes[2]; - - /* Timestamp and index into store file */ - struct broadcastable bcast; - - struct amount_sat sat; -}; - -/* Use this instead of tal_free(chan)! */ -void free_chan(struct routing_state *rstate, struct chan *chan); - -static inline bool is_halfchan_defined(const struct half_chan *hc) -{ - return hc->bcast.index != 0; -} - -/* Container for per-node channel pointers. Better cache performance - * than uintmap, and we don't need ordering. */ -static inline const struct short_channel_id *chan_map_scid(const struct chan *c) -{ - return &c->scid; -} - -static inline size_t hash_scid(const struct short_channel_id *scid) -{ - /* scids cost money to generate, so simple hash works here */ - return (scid->u64 >> 32) ^ (scid->u64 >> 16) ^ scid->u64; -} - -static inline bool chan_eq_scid(const struct chan *c, - const struct short_channel_id *scid) -{ - return short_channel_id_eq(scid, &c->scid); -} - -HTABLE_DEFINE_TYPE(struct chan, chan_map_scid, hash_scid, chan_eq_scid, chan_map); - -struct node { - struct node_id id; - - /* Timestamp and index into store file */ - struct broadcastable bcast; - - /* Possibly spam flagged. Nonbroadcastable, but used for routing graph. - * If there is no current spam, rgraph == bcast. */ - struct broadcastable rgraph; - - /* Token bucket */ - u8 tokens; - - /* Channels connecting us to other nodes */ - /* For a small number of channels (by far the most common) we - * use a simple array, with empty buckets NULL. For larger, we use a - * proper hash table, with the extra allocations that implies. - * - * As of November 2022, 5 or 6 gives the optimal size. - */ - struct chan *chan_arr[6]; - /* If we have more than that, we use a hash. */ - struct chan_map *chan_map; -}; - -const struct node_id *node_map_keyof_node(const struct node *n); -size_t node_map_hash_key(const struct node_id *pc); -bool node_map_node_eq(const struct node *n, const struct node_id *pc); -HTABLE_DEFINE_TYPE(struct node, node_map_keyof_node, node_map_hash_key, node_map_node_eq, node_map); - -/* We've unpacked and checked its signatures, now we wait for master to tell - * us the txout to check */ -struct pending_cannouncement { - /* Unpacked fields here */ - - /* also the key in routing_state->pending_cannouncements */ - struct short_channel_id short_channel_id; - struct node_id node_id_1; - struct node_id node_id_2; - struct pubkey bitcoin_key_1; - struct pubkey bitcoin_key_2; - - struct node_id *source_peer; - - /* The raw bits */ - const u8 *announce; - - /* Deferred updates, if we received them while waiting for - * this (one for each direction) */ - const u8 *updates[2]; - /* Peers responsible */ - struct node_id *update_source_peer[2]; - - /* Only ever replace with newer updates */ - u32 update_timestamps[2]; -}; - -static inline const struct short_channel_id *panding_cannouncement_map_scid( - const struct pending_cannouncement *pending_ann) -{ - return &pending_ann->short_channel_id; -} - -static inline size_t hash_pending_cannouncement_scid( - const struct short_channel_id *scid) -{ - /* like hash_scid() for struct chan above */ - return (scid->u64 >> 32) ^ (scid->u64 >> 16) ^ scid->u64; -} - -static inline bool pending_cannouncement_eq_scid( - const struct pending_cannouncement *pending_ann, - const struct short_channel_id *scid) -{ - return short_channel_id_eq(scid, &pending_ann->short_channel_id); -} - -HTABLE_DEFINE_TYPE(struct pending_cannouncement, panding_cannouncement_map_scid, - hash_pending_cannouncement_scid, pending_cannouncement_eq_scid, - pending_cannouncement_map); - -struct pending_node_map; -struct unupdated_channel; - -/* If you know n is one end of the channel, get index of src == n */ -static inline int half_chan_idx(const struct node *n, const struct chan *chan) -{ - int idx = (chan->nodes[1] == n); - - assert(chan->nodes[0] == n || chan->nodes[1] == n); - return idx; -} - -struct routing_state { - struct daemon *daemon; - - /* All known nodes. */ - struct node_map *nodes; - - /* node_announcements which are waiting on pending_cannouncement */ - struct pending_node_map *pending_node_map; - - /* channel_announcement which are pending short_channel_id lookup */ - struct pending_cannouncement_map *pending_cannouncements; - - /* Gossip store */ - struct gossip_store *gs; - - /* A map of channels indexed by short_channel_ids */ - UINTMAP(struct chan *) chanmap; - - /* A map of channel_announcements indexed by short_channel_ids: - * we haven't got a channel_update for these yet. */ - UINTMAP(struct unupdated_channel *) unupdated_chanmap; - - /* Cache for txout queries that failed. Allows us to skip failed - * checks if we get another announcement for the same scid. */ - size_t num_txout_failures; - UINTMAP(bool) txout_failures, txout_failures_old; - struct oneshot *txout_failure_timer; - - /* Highest timestamp of gossip we accepted (before now) */ - u32 last_timestamp; - - /* Channels which are closed, but we're waiting 12 blocks */ - struct dying_channel *dying_channels; - - /* Override local time for gossip messages */ - struct timeabs *dev_gossip_time; - - /* Speed up gossip. */ - bool dev_fast_gossip; - - /* Speed up pruning. */ - bool dev_fast_gossip_prune; -}; - -/* Which direction are we? False if neither. */ -static inline bool local_direction(struct routing_state *rstate, - const struct chan *chan, - int *direction) -{ - for (int dir = 0; dir <= 1; (dir)++) { - if (node_id_eq(&chan->nodes[dir]->id, &rstate->daemon->id)) { - if (direction) - *direction = dir; - return true; - } - } - return false; -} - -static inline struct chan * -get_channel(const struct routing_state *rstate, - const struct short_channel_id *scid) -{ - return uintmap_get(&rstate->chanmap, scid->u64); -} - -struct routing_state *new_routing_state(const tal_t *ctx, - struct daemon *daemon, - const u32 *dev_gossip_time TAKES, - bool dev_fast_gossip, - bool dev_fast_gossip_prune); - -/** - * Add a new bidirectional channel from id1 to id2 with the given - * short_channel_id and capacity to the local network view. The channel may not - * already exist, and might create the node entries for the two endpoints, if - * they do not exist yet. - */ -struct chan *new_chan(struct routing_state *rstate, - const struct short_channel_id *scid, - const struct node_id *id1, - const struct node_id *id2, - struct amount_sat sat); - -/* Handlers for incoming messages */ - -/** - * handle_channel_announcement -- Check channel announcement is valid - * - * Returns error message if we should fail channel. Make *scid non-NULL - * (for checking) if we extracted a short_channel_id, otherwise ignore. - */ -u8 *handle_channel_announcement(struct routing_state *rstate, - const u8 *announce TAKES, - u32 current_blockheight, - const struct short_channel_id **scid, - const struct node_id *source_peer TAKES); - -/** - * handle_pending_cannouncement -- handle channel_announce once we've - * completed short_channel_id lookup. Returns true if handling created - * a new channel. - */ -bool handle_pending_cannouncement(struct daemon *daemon, - struct routing_state *rstate, - const struct short_channel_id *scid, - const struct amount_sat sat, - const u8 *txscript); - -/* Iterate through channels in a node */ -struct chan *first_chan(const struct node *node, struct chan_map_iter *i); -struct chan *next_chan(const struct node *node, struct chan_map_iter *i); - -/* Returns NULL if all OK, otherwise an error for the peer which sent. - * If the error is that the channel is unknown, fills in *unknown_scid - * (if not NULL). */ -u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, - const struct node_id *source_peer TAKES, - struct short_channel_id *unknown_scid, - bool force); - -/* Returns NULL if all OK, otherwise an error for the peer which sent. - * If was_unknown is not NULL, sets it to true if that was the reason for - * the error: the node was unknown to us. */ -u8 *handle_node_announcement(struct routing_state *rstate, const u8 *node_ann, - const struct node_id *source_peer TAKES, - bool *was_unknown); - -/* Get a node: use this instead of node_map_get() */ -struct node *get_node(struct routing_state *rstate, - const struct node_id *id); - -void route_prune(struct routing_state *rstate); - -/** - * Add a channel_announcement to the network view without checking it - * - * Directly add the channel to the local network, without checking it first. Use - * this only for messages from trusted sources. Untrusted sources should use the - * @see{handle_channel_announcement} entrypoint to check before adding. - * - * index is usually 0, in which case it's set by insert_broadcast adding it - * to the store. - * - * source_peer is an optional peer responsible for this. - */ -bool routing_add_channel_announcement(struct routing_state *rstate, - const u8 *msg TAKES, - struct amount_sat sat, - u32 index, - const struct node_id *source_peer TAKES); - -/** - * Add a channel_update without checking for errors - * - * Used to actually insert the information in the channel update into the local - * network view. Only use this for messages that are known to be good. For - * untrusted source, requiring verification please use - * @see{handle_channel_update} - */ -bool routing_add_channel_update(struct routing_state *rstate, - const u8 *update TAKES, - u32 index, - const struct node_id *source_peer TAKES, - bool ignore_timestamp, - bool force_spam_flag, - bool force_zombie_flag); -/** - * Add a node_announcement to the network view without checking it - * - * Directly add the node being announced to the network view, without verifying - * it. This must be from a trusted source, e.g., gossip_store. For untrusted - * sources (peers) please use @see{handle_node_announcement}. - */ -bool routing_add_node_announcement(struct routing_state *rstate, - const u8 *msg TAKES, - u32 index, - const struct node_id *source_peer TAKES, - bool *was_unknown, - bool force_spam_flag); - -/** - * Get the local time. - * - * This gets overridden in dev mode so we can use canned (stale) gossip. - */ -struct timeabs gossip_time_now(const struct routing_state *rstate); - -/** - * Add to rstate->dying_channels - * - * Exposed here for when we load the gossip_store. - */ -void remember_chan_dying(struct routing_state *rstate, - const struct short_channel_id *scid, - u32 deadline_blockheight, - u64 index); - -/** - * When a channel's funding has been spent. - */ -void routing_channel_spent(struct routing_state *rstate, - u32 current_blockheight, - struct chan *chan); - -/** - * Clean up any dying channels. - * - * This finally deletes channel past their deadline. - */ -void routing_expire_channels(struct routing_state *rstate, u32 blockheight); - -/* Would we ratelimit a channel_update with this timestamp? */ -bool would_ratelimit_cupdate(struct routing_state *rstate, - const struct half_chan *hc, - u32 timestamp); - -/* Does this node have public, non-zombie channels? */ -bool node_has_broadcastable_channels(const struct node *node); - -/* Returns an error string if there are unfinalized entries after load */ -const char *unfinalized_entries(const tal_t *ctx, struct routing_state *rstate); - -void remove_all_gossip(struct routing_state *rstate); - -/* We have an update for one of our channels (or unknown). */ -void tell_lightningd_peer_update(struct routing_state *rstate, - const struct node_id *source_peer, - struct short_channel_id scid, - u32 fee_base_msat, - u32 fee_ppm, - u16 cltv_delta, - struct amount_msat htlc_minimum, - struct amount_msat htlc_maximum); -#endif /* LIGHTNING_GOSSIPD_ROUTING_H */ diff --git a/gossipd/seeker.c b/gossipd/seeker.c index 159b0a2c1cb9..f23bc1268010 100644 --- a/gossipd/seeker.c +++ b/gossipd/seeker.c @@ -3,20 +3,22 @@ #include #include #include +#include #include #include +#include #include #include #include #include #include #include +#include #include -#include #include #define GOSSIP_SEEKER_INTERVAL(seeker) \ - DEV_FAST_GOSSIP((seeker)->daemon->rstate->dev_fast_gossip, 5, 60) + DEV_FAST_GOSSIP((seeker)->daemon->dev_fast_gossip, 5, 60) enum seeker_state { /* Still streaming gossip from single peer. */ @@ -204,29 +206,33 @@ static void disable_gossip_stream(struct seeker *seeker, struct peer *peer) &chainparams->genesis_blockhash, UINT32_MAX, UINT32_MAX); - queue_peer_msg(peer, take(msg)); + queue_peer_msg(peer->daemon, &peer->id, take(msg)); } static void enable_gossip_stream(struct seeker *seeker, struct peer *peer) { - /* We seek some way back, to take into account propagation time */ - const u32 polltime = GOSSIP_SEEKER_INTERVAL(seeker) * 10; - u32 start = seeker->daemon->rstate->last_timestamp; + u32 start; u8 *msg; - if (start > polltime) - start -= polltime; - else + /* Modern timestamp_filter is a trinary: 0 = all, FFFFFFFF = none, + * other = from now on */ + if (seeker->daemon->gossip_store_populated) { + /* Just in case they care */ + start = time_now().ts.tv_sec - GOSSIP_SEEKER_INTERVAL(seeker) * 10; + } else { start = 0; + } - status_peer_debug(&peer->id, "seeker: starting gossip"); + status_peer_debug(&peer->id, "seeker: starting gossip (%s)", + seeker->daemon->gossip_store_populated + ? "streaming" : "EVERTHING"); /* This is allowed even if they don't understand it (odd) */ msg = towire_gossip_timestamp_filter(NULL, &chainparams->genesis_blockhash, start, UINT32_MAX); - queue_peer_msg(peer, take(msg)); + queue_peer_msg(peer->daemon, &peer->id, take(msg)); } static void normal_gossip_start(struct seeker *seeker, struct peer *peer) @@ -437,33 +443,38 @@ static int cmp_scid(const struct short_channel_id *a, /* We can't ask for channels by node_id, so probe at random */ static bool get_unannounced_nodes(const tal_t *ctx, - struct routing_state *rstate, + struct daemon *daemon, size_t max, struct short_channel_id **scids, u8 **query_flags) { - size_t num = 0; - u64 offset; - double total_weight = 0.0; + size_t num = 0, off, max_idx; + struct gossmap *gossmap = gossmap_manage_get_gossmap(daemon->gm); + + /* No nodes? Nothing to ask for */ + max_idx = gossmap_max_node_idx(gossmap); + if (max_idx == 0 || max == 0) + return false; - /* Pick an example short_channel_id at random to query. As a - * side-effect this gets the node. */ + /* Start at random index */ + off = pseudorand(max_idx); *scids = tal_arr(ctx, struct short_channel_id, max); - /* FIXME: This is inefficient! Reuse next_block_range here! */ - for (struct chan *c = uintmap_first(&rstate->chanmap, &offset); - c; - c = uintmap_after(&rstate->chanmap, &offset)) { - if (c->nodes[0]->bcast.index && c->nodes[1]->bcast.index) + for (size_t i = 0; i < max_idx; i++) { + const struct gossmap_node *node = gossmap_node_byidx(gossmap, (off + i) % max_idx); + const struct gossmap_chan *chan; + + if (!node) + continue; + if (gossmap_node_announced(node)) continue; - if (num < max) { - (*scids)[num++] = c->scid; - } else { - /* Maybe replace one: approx. reservoir sampling */ - if (random_select(1.0, &total_weight)) - (*scids)[pseudorand(max)] = c->scid; - } + /* Query first chan. */ + chan = gossmap_nth_chan(gossmap, node, 0, NULL); + (*scids)[num++] = gossmap_chan_scid(gossmap, chan); + + if (num == max) + break; } if (num == 0) { @@ -474,18 +485,23 @@ static bool get_unannounced_nodes(const tal_t *ctx, if (num < max) tal_resize(scids, num); - /* Sort them into order. */ + /* Sort them into order, and remove duplicates! */ asort(*scids, num, cmp_scid, NULL); + for (size_t i = 1; i < tal_count(*scids); i++) { + if (short_channel_id_eq(&(*scids)[i], &(*scids)[i-1])) { + tal_arr_remove(scids, i); + } + } /* Now get flags. */ - *query_flags = tal_arr(ctx, u8, num); + *query_flags = tal_arr(ctx, u8, tal_count(*scids)); for (size_t i = 0; i < tal_count(*scids); i++) { - struct chan *c = get_channel(rstate, &(*scids)[i]); + const struct gossmap_chan *chan = gossmap_find_chan(gossmap, &(*scids)[i]); (*query_flags)[i] = 0; - if (!c->nodes[0]->bcast.index) + if (!gossmap_node_announced(gossmap_nth_node(gossmap, chan, 0))) (*query_flags)[i] |= SCID_QF_NODE1; - if (!c->nodes[1]->bcast.index) + if (!gossmap_node_announced(gossmap_nth_node(gossmap, chan, 1))) (*query_flags)[i] |= SCID_QF_NODE2; } return true; @@ -496,9 +512,10 @@ static void peer_gossip_probe_nannounces(struct seeker *seeker); static void nodeannounce_query_done(struct peer *peer, bool complete) { - struct seeker *seeker = peer->daemon->seeker; - struct routing_state *rstate = seeker->daemon->rstate; + struct daemon *daemon = peer->daemon; + struct seeker *seeker = daemon->seeker; size_t new_nannounce = 0, num_scids; + struct gossmap *gossmap = gossmap_manage_get_gossmap(daemon->gm); /* We might have given up on them, then they replied. */ if (seeker->random_peer != peer) { @@ -510,16 +527,16 @@ static void nodeannounce_query_done(struct peer *peer, bool complete) num_scids = tal_count(seeker->nannounce_scids); for (size_t i = 0; i < num_scids; i++) { - struct chan *c = get_channel(rstate, - &seeker->nannounce_scids[i]); + struct gossmap_chan *c = gossmap_find_chan(gossmap, + &seeker->nannounce_scids[i]); /* Could have closed since we asked. */ if (!c) continue; if ((seeker->nannounce_query_flags[i] & SCID_QF_NODE1) - && c->nodes[0]->bcast.index) + && gossmap_node_announced(gossmap_nth_node(gossmap, c, 0))) new_nannounce++; if ((seeker->nannounce_query_flags[i] & SCID_QF_NODE2) - && c->nodes[1]->bcast.index) + && gossmap_node_announced(gossmap_nth_node(gossmap, c, 1))) new_nannounce++; } @@ -546,7 +563,7 @@ static void nodeannounce_query_done(struct peer *peer, bool complete) if (num_scids > 7000) num_scids = 7000; - if (!get_unannounced_nodes(seeker, seeker->daemon->rstate, num_scids, + if (!get_unannounced_nodes(seeker, seeker->daemon, num_scids, &seeker->nannounce_scids, &seeker->nannounce_query_flags)) { /* Nothing unknown at all? Great, we're done */ @@ -579,26 +596,34 @@ static void peer_gossip_probe_nannounces(struct seeker *seeker) } /* They have update with this timestamp: do we want it? */ -static bool want_update(struct seeker *seeker, - u32 timestamp, const struct half_chan *hc) +static bool want_update(struct gossmap *gossmap, + u32 timestamp, + const struct gossmap_chan *chan, + int dir) { - if (!is_halfchan_defined(hc)) + u32 our_timestamp; + + if (!gossmap_chan_set(chan, dir)) return timestamp != 0; - if (timestamp <= hc->bcast.timestamp) + gossmap_chan_get_update_details(gossmap, chan, dir, &our_timestamp, + NULL, NULL, NULL, NULL, NULL, NULL); + if (timestamp <= our_timestamp) return false; - return !would_ratelimit_cupdate(seeker->daemon->rstate, hc, timestamp); + return true; } /* They gave us timestamps. Do we want updated versions? */ static void check_timestamps(struct seeker *seeker, - struct chan *c, + struct gossmap *gossmap, + struct gossmap_chan *c, const struct channel_update_timestamps *ts, struct peer *peer) { u8 *stale; u8 query_flag = 0; + struct short_channel_id scid; /* BOLT #7: * * `timestamp_node_id_1` is the timestamp of the `channel_update` @@ -608,46 +633,63 @@ static void check_timestamps(struct seeker *seeker, * for `node_id_2`, or 0 if there was no `channel_update` from that * node. */ - if (want_update(seeker, ts->timestamp_node_id_1, &c->half[0])) + if (want_update(gossmap, ts->timestamp_node_id_1, c, 0)) query_flag |= SCID_QF_UPDATE1; - if (want_update(seeker, ts->timestamp_node_id_2, &c->half[1])) + if (want_update(gossmap, ts->timestamp_node_id_2, c, 1)) query_flag |= SCID_QF_UPDATE2; if (!query_flag) return; /* Add in flags if we're already getting it. */ - stale = uintmap_get(&seeker->stale_scids, c->scid.u64); + scid = gossmap_chan_scid(gossmap, c); + stale = uintmap_get(&seeker->stale_scids, scid.u64); if (!stale) { stale = talz(seeker, u8); - uintmap_add(&seeker->stale_scids, c->scid.u64, stale); + uintmap_add(&seeker->stale_scids, scid.u64, stale); set_preferred_peer(seeker, peer); } *stale |= query_flag; } +static bool add_unknown_scid(struct seeker *seeker, + struct short_channel_id scid, + struct peer *peer) +{ + /* Check we're not already getting this one. */ + if (!uintmap_add(&seeker->unknown_scids, scid.u64, true)) + return false; + + set_preferred_peer(seeker, peer); + return true; +} + static void process_scid_probe(struct peer *peer, u32 first_blocknum, u32 number_of_blocks, const struct range_query_reply *replies) { - struct seeker *seeker = peer->daemon->seeker; + struct daemon *daemon = peer->daemon; + struct seeker *seeker = daemon->seeker; bool new_unknown_scids = false; + struct gossmap *gossmap; /* We might have given up on them, then they replied. */ if (seeker->random_peer != peer) return; seeker->random_peer = NULL; + gossmap = gossmap_manage_get_gossmap(daemon->gm); for (size_t i = 0; i < tal_count(replies); i++) { - struct chan *c = get_channel(seeker->daemon->rstate, - &replies[i].scid); + struct gossmap_chan *c = gossmap_find_chan(gossmap, + &replies[i].scid); if (c) { - check_timestamps(seeker, c, &replies[i].ts, peer); + check_timestamps(seeker, gossmap, c, &replies[i].ts, peer); continue; } - new_unknown_scids |= add_unknown_scid(seeker, &replies[i].scid, + new_unknown_scids |= add_unknown_scid(seeker, + replies[i].scid, peer); } @@ -669,7 +711,7 @@ static void process_scid_probe(struct peer *peer, } /* Channel probe finished, try asking for 128 unannounced nodes. */ - if (!get_unannounced_nodes(seeker, seeker->daemon->rstate, 128, + if (!get_unannounced_nodes(seeker, seeker->daemon, 128, &seeker->nannounce_scids, &seeker->nannounce_query_flags)) { /* No unknown nodes. Great! */ @@ -932,34 +974,26 @@ bool remove_unknown_scid(struct seeker *seeker, return uintmap_del(&seeker->unknown_scids, scid->u64); } -bool add_unknown_scid(struct seeker *seeker, - const struct short_channel_id *scid, - struct peer *peer) -{ - /* Check we're not already getting this one. */ - if (!uintmap_add(&seeker->unknown_scids, scid->u64, true)) - return false; - - set_preferred_peer(seeker, peer); - return true; -} - /* This peer told us about an update to an unknown channel. Ask it for a * channel_announcement. */ void query_unknown_channel(struct daemon *daemon, - struct peer *peer, - const struct short_channel_id *id) + const struct node_id *source_peer, + struct short_channel_id unknown_scid) { - /* Too many, or duplicate? */ - if (!add_unknown_scid(daemon->seeker, id, peer)) - return; + add_unknown_scid(daemon->seeker, + unknown_scid, + source_peer ? find_peer(daemon, source_peer) : NULL); } /* This peer told us about an unknown node. Start probing it. */ -void query_unknown_node(struct seeker *seeker, struct peer *peer) -{ - seeker->unknown_nodes = true; - set_preferred_peer(seeker, peer); +void query_unknown_node(struct daemon *daemon, + const struct node_id *source_peer, + const struct node_id *unknown_node) +{ + daemon->seeker->unknown_nodes = true; + if (source_peer) + set_preferred_peer(daemon->seeker, + find_peer(daemon, source_peer)); } /* Peer has died, NULL out any pointers we have */ diff --git a/gossipd/seeker.h b/gossipd/seeker.h index 2edd2662355d..90afde80520d 100644 --- a/gossipd/seeker.h +++ b/gossipd/seeker.h @@ -3,25 +3,26 @@ #include "config.h" struct daemon; -struct peer; +struct node_id; struct short_channel_id; struct seeker *new_seeker(struct daemon *daemon); +/* source_peer can be NULL! */ void query_unknown_channel(struct daemon *daemon, - struct peer *peer, - const struct short_channel_id *id); + const struct node_id *source_peer, + const struct short_channel_id unknown_scid); -void query_unknown_node(struct seeker *seeker, struct peer *peer); +/* source_peer can be NULL! */ +void query_unknown_node(struct daemon *daemon, + const struct node_id *source_peer, + const struct node_id *unknown_node); void seeker_setup_peer_gossip(struct seeker *seeker, struct peer *peer); bool remove_unknown_scid(struct seeker *seeker, const struct short_channel_id *scid, bool found); -bool add_unknown_scid(struct seeker *seeker, - const struct short_channel_id *scid, - struct peer *peer); void seeker_peer_gone(struct seeker *seeker, const struct peer *peer); diff --git a/gossipd/sigcheck.c b/gossipd/sigcheck.c new file mode 100644 index 000000000000..1ca94fdecb65 --- /dev/null +++ b/gossipd/sigcheck.c @@ -0,0 +1,184 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include + +/* Verify the signature of a channel_update message */ +const char *sigcheck_channel_update(const tal_t *ctx, + const struct node_id *node_id, + const secp256k1_ecdsa_signature *node_sig, + const u8 *update) +{ + /* BOLT #7: + * 1. type: 258 (`channel_update`) + * 2. data: + * * [`signature`:`signature`] + * * [`chain_hash`:`chain_hash`] + * * [`short_channel_id`:`short_channel_id`] + * * [`u32`:`timestamp`] + * * [`byte`:`message_flags`] + * * [`byte`:`channel_flags`] + * * [`u16`:`cltv_expiry_delta`] + * * [`u64`:`htlc_minimum_msat`] + * * [`u32`:`fee_base_msat`] + * * [`u32`:`fee_proportional_millionths`] + * * [`u64`:`htlc_maximum_msat`] + */ + /* 2 byte msg type + 64 byte signatures */ + int offset = 66; + struct sha256_double hash; + + sha256_double(&hash, update + offset, tal_count(update) - offset); + + if (!check_signed_hash_nodeid(&hash, node_sig, node_id)) + return tal_fmt(ctx, + "Bad signature for %s hash %s" + " on channel_update %s", + type_to_string(tmpctx, + secp256k1_ecdsa_signature, + node_sig), + type_to_string(tmpctx, + struct sha256_double, + &hash), + tal_hex(tmpctx, update)); + return NULL; +} + +const char *sigcheck_channel_announcement(const tal_t *ctx, + const struct node_id *node1_id, + const struct node_id *node2_id, + const struct pubkey *bitcoin1_key, + const struct pubkey *bitcoin2_key, + const secp256k1_ecdsa_signature *node1_sig, + const secp256k1_ecdsa_signature *node2_sig, + const secp256k1_ecdsa_signature *bitcoin1_sig, + const secp256k1_ecdsa_signature *bitcoin2_sig, + const u8 *announcement) +{ + /* BOLT #7: + * 1. type: 256 (`channel_announcement`) + * 2. data: + * * [`signature`:`node_signature_1`] + * * [`signature`:`node_signature_2`] + * * [`signature`:`bitcoin_signature_1`] + * * [`signature`:`bitcoin_signature_2`] + * * [`u16`:`len`] + * * [`len*byte`:`features`] + * * [`chain_hash`:`chain_hash`] + * * [`short_channel_id`:`short_channel_id`] + * * [`point`:`node_id_1`] + * * [`point`:`node_id_2`] + * * [`point`:`bitcoin_key_1`] + * * [`point`:`bitcoin_key_2`] + */ + /* 2 byte msg type + 256 byte signatures */ + int offset = 258; + struct sha256_double hash; + sha256_double(&hash, announcement + offset, + tal_count(announcement) - offset); + + if (!check_signed_hash_nodeid(&hash, node1_sig, node1_id)) { + return tal_fmt(ctx, + "Bad node_signature_1 %s hash %s" + " on channel_announcement %s", + type_to_string(tmpctx, + secp256k1_ecdsa_signature, + node1_sig), + type_to_string(tmpctx, + struct sha256_double, + &hash), + tal_hex(tmpctx, announcement)); + } + if (!check_signed_hash_nodeid(&hash, node2_sig, node2_id)) { + return tal_fmt(ctx, + "Bad node_signature_2 %s hash %s" + " on channel_announcement %s", + type_to_string(tmpctx, + secp256k1_ecdsa_signature, + node2_sig), + type_to_string(tmpctx, + struct sha256_double, + &hash), + tal_hex(tmpctx, announcement)); + } + if (!check_signed_hash(&hash, bitcoin1_sig, bitcoin1_key)) { + return tal_fmt(ctx, + "Bad bitcoin_signature_1 %s hash %s" + " on channel_announcement %s", + type_to_string(tmpctx, + secp256k1_ecdsa_signature, + bitcoin1_sig), + type_to_string(tmpctx, + struct sha256_double, + &hash), + tal_hex(tmpctx, announcement)); + } + if (!check_signed_hash(&hash, bitcoin2_sig, bitcoin2_key)) { + return tal_fmt(ctx, + "Bad bitcoin_signature_2 %s hash %s" + " on channel_announcement %s", + type_to_string(tmpctx, + secp256k1_ecdsa_signature, + bitcoin2_sig), + type_to_string(tmpctx, + struct sha256_double, + &hash), + tal_hex(tmpctx, announcement)); + } + return NULL; +} + +/* Returns warning msg if signature wrong, else NULL */ +const char *sigcheck_node_announcement(const tal_t *ctx, + const struct node_id *node_id, + const secp256k1_ecdsa_signature *signature, + const u8 *node_announcement) +{ + /* BOLT #7: + * + * 1. type: 257 (`node_announcement`) + * 2. data: + * * [`signature`:`signature`] + * * [`u16`:`flen`] + * * [`flen*byte`:`features`] + * * [`u32`:`timestamp`] + * * [`point`:`node_id`] + * * [`3*byte`:`rgb_color`] + * * [`32*byte`:`alias`] + * * [`u16`:`addrlen`] + * * [`addrlen*byte`:`addresses`] + */ + /* 2 byte msg type + 64 byte signatures */ + int offset = 66; + struct sha256_double hash; + + sha256_double(&hash, node_announcement + offset, tal_count(node_announcement) - offset); + /* If node_id is invalid, it fails here */ + if (!check_signed_hash_nodeid(&hash, signature, node_id)) { + /* BOLT #7: + * + * - if `signature` is not a valid signature, using + * `node_id` of the double-SHA256 of the entire + * message following the `signature` field + * (including unknown fields following + * `fee_proportional_millionths`): + * - SHOULD send a `warning` and close the connection. + * - MUST NOT process the message further. + */ + return tal_fmt(ctx, + "Bad signature for %s hash %s" + " on node_announcement %s", + type_to_string(tmpctx, + secp256k1_ecdsa_signature, + signature), + type_to_string(tmpctx, + struct sha256_double, + &hash), + tal_hex(tmpctx, node_announcement)); + } + + return NULL; +} diff --git a/gossipd/sigcheck.h b/gossipd/sigcheck.h new file mode 100644 index 000000000000..ac15b831e55c --- /dev/null +++ b/gossipd/sigcheck.h @@ -0,0 +1,29 @@ +#ifndef LIGHTNING_GOSSIPD_SIGCHECK_H +#define LIGHTNING_GOSSIPD_SIGCHECK_H +#include "config.h" +#include + +/* Returns error msg if signature wrong, else NULL */ +const char *sigcheck_channel_update(const tal_t *ctx, + const struct node_id *node_id, + const secp256k1_ecdsa_signature *node_sig, + const u8 *update); + +/* Returns error msg if signature wrong, else NULL */ +const char *sigcheck_channel_announcement(const tal_t *ctx, + const struct node_id *node1_id, + const struct node_id *node2_id, + const struct pubkey *bitcoin1_key, + const struct pubkey *bitcoin2_key, + const secp256k1_ecdsa_signature *node1_sig, + const secp256k1_ecdsa_signature *node2_sig, + const secp256k1_ecdsa_signature *bitcoin1_sig, + const secp256k1_ecdsa_signature *bitcoin2_sig, + const u8 *announcement); + +/* Returns error msg if signature wrong, else NULL */ +const char *sigcheck_node_announcement(const tal_t *ctx, + const struct node_id *node_id, + const secp256k1_ecdsa_signature *node_sig, + const u8 *node_announcement); +#endif /* LIGHTNING_GOSSIPD_SIGCHECK_H */ diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index 95eb5a2a0ede..284d1d21b434 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -29,7 +29,8 @@ In particular, we set feature bit 19. The spec says we should set feature bit 1 #include "config.h" #include "../common/wire_error.c" -#include "../routing.c" +#include "../sigcheck.c" +#include #include #include #include @@ -54,79 +55,13 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, const struct sha256 *h UNNEEDED, struct pubkey *next UNNEEDED) { fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } -/* Generated stub for daemon_conn_send */ -void daemon_conn_send(struct daemon_conn *dc UNNEEDED, const u8 *msg UNNEEDED) -{ fprintf(stderr, "daemon_conn_send called!\n"); abort(); } -/* Generated stub for get_cupdate_parts */ -void get_cupdate_parts(const u8 *channel_update UNNEEDED, - const u8 *parts[2] UNNEEDED, - size_t sizes[2]) -{ fprintf(stderr, "get_cupdate_parts called!\n"); abort(); } -/* Generated stub for gossip_store_add */ -u64 gossip_store_add(struct gossip_store *gs UNNEEDED, const u8 *gossip_msg UNNEEDED, - u32 timestamp UNNEEDED, bool zombie UNNEEDED, bool spam UNNEEDED, bool dying UNNEEDED, - const u8 *addendum UNNEEDED) -{ fprintf(stderr, "gossip_store_add called!\n"); abort(); } -/* Generated stub for gossip_store_delete */ -void gossip_store_delete(struct gossip_store *gs UNNEEDED, - struct broadcastable *bcast UNNEEDED, - int type UNNEEDED) -{ fprintf(stderr, "gossip_store_delete called!\n"); abort(); } -/* Generated stub for gossip_store_get */ -const u8 *gossip_store_get(const tal_t *ctx UNNEEDED, - struct gossip_store *gs UNNEEDED, - u64 offset UNNEEDED) -{ fprintf(stderr, "gossip_store_get called!\n"); abort(); } -/* Generated stub for gossip_store_mark_channel_deleted */ -void gossip_store_mark_channel_deleted(struct gossip_store *gs UNNEEDED, - const struct short_channel_id *scid UNNEEDED) -{ fprintf(stderr, "gossip_store_mark_channel_deleted called!\n"); abort(); } -/* Generated stub for gossip_store_mark_dying */ -void gossip_store_mark_dying(struct gossip_store *gs UNNEEDED, - const struct broadcastable *bcast UNNEEDED, - int type UNNEEDED) -{ fprintf(stderr, "gossip_store_mark_dying called!\n"); abort(); } -/* Generated stub for gossip_store_new */ -struct gossip_store *gossip_store_new(struct routing_state *rstate UNNEEDED) -{ fprintf(stderr, "gossip_store_new called!\n"); abort(); } -/* Generated stub for memleak_add_helper_ */ -void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, - const tal_t *)){ } -/* Generated stub for memleak_scan_htable */ -void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_scan_htable called!\n"); abort(); } -/* Generated stub for memleak_scan_intmap_ */ -void memleak_scan_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) -{ fprintf(stderr, "memleak_scan_intmap_ called!\n"); abort(); } -/* Generated stub for new_reltimer_ */ -struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, - const tal_t *ctx UNNEEDED, - struct timerel expire UNNEEDED, - void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) -{ fprintf(stderr, "new_reltimer_ called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } -/* Generated stub for peer_supplied_good_gossip */ -void peer_supplied_good_gossip(struct daemon *daemon UNNEEDED, - const struct node_id *source_peer UNNEEDED, - size_t amount UNNEEDED) -{ fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } -/* Generated stub for status_fmt */ -void status_fmt(enum log_level level UNNEEDED, - const struct node_id *peer UNNEEDED, - const char *fmt UNNEEDED, ...) - -{ fprintf(stderr, "status_fmt called!\n"); abort(); } -/* Generated stub for towire_gossipd_remote_channel_update */ -u8 *towire_gossipd_remote_channel_update(const tal_t *ctx UNNEEDED, const struct node_id *source_node UNNEEDED, const struct peer_update *peer_update UNNEEDED) -{ fprintf(stderr, "towire_gossipd_remote_channel_update called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ int main(int argc, char *argv[]) { struct bitcoin_blkid chain_hash; - u8 *features, *err; + u8 *features; + const char *err; secp256k1_ecdsa_signature node_signature_1, node_signature_2; secp256k1_ecdsa_signature bitcoin_signature_1, bitcoin_signature_2; struct short_channel_id short_channel_id; @@ -151,16 +86,15 @@ int main(int argc, char *argv[]) &bitcoin_key_2)) abort(); - err = check_channel_announcement(cannounce, - &node_id_1, &node_id_2, - &bitcoin_key_1, &bitcoin_key_2, - &node_signature_1, &node_signature_2, - &bitcoin_signature_1, - &bitcoin_signature_2, - cannounce); + err = sigcheck_channel_announcement(cannounce, + &node_id_1, &node_id_2, + &bitcoin_key_1, &bitcoin_key_2, + &node_signature_1, &node_signature_2, + &bitcoin_signature_1, + &bitcoin_signature_2, + cannounce); assert(err); - assert(memmem(err, tal_bytelen(err), - "Bad node_signature_1", strlen("Bad node_signature_1"))); + assert(strstr(err, "Bad node_signature_1")); /* Turns out they didn't include the feature bit at all. */ cannounce = towire_channel_announcement(tmpctx, @@ -175,16 +109,15 @@ int main(int argc, char *argv[]) &node_id_2, &bitcoin_key_1, &bitcoin_key_2); - err = check_channel_announcement(cannounce, - &node_id_1, &node_id_2, - &bitcoin_key_1, &bitcoin_key_2, - &node_signature_1, &node_signature_2, - &bitcoin_signature_1, - &bitcoin_signature_2, - cannounce); + err = sigcheck_channel_announcement(cannounce, + &node_id_1, &node_id_2, + &bitcoin_key_1, &bitcoin_key_2, + &node_signature_1, &node_signature_2, + &bitcoin_signature_1, + &bitcoin_signature_2, + cannounce); assert(err); - assert(memmem(err, tal_bytelen(err), - "Bad node_signature_2", strlen("Bad node_signature_2"))); + assert(strstr(err, "Bad node_signature_2")); common_shutdown(); return 0; diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index c14c5f360cf5..0f0a05028917 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -48,15 +48,65 @@ struct peer *first_random_peer(struct daemon *daemon UNNEEDED, /* Generated stub for fromwire_gossipd_dev_set_max_scids_encode_size */ bool fromwire_gossipd_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED) { fprintf(stderr, "fromwire_gossipd_dev_set_max_scids_encode_size called!\n"); abort(); } -/* Generated stub for get_node */ -struct node *get_node(struct routing_state *rstate UNNEEDED, - const struct node_id *id UNNEEDED) -{ fprintf(stderr, "get_node called!\n"); abort(); } -/* Generated stub for gossip_store_get */ -const u8 *gossip_store_get(const tal_t *ctx UNNEEDED, - struct gossip_store *gs UNNEEDED, - u64 offset UNNEEDED) -{ fprintf(stderr, "gossip_store_get called!\n"); abort(); } +/* Generated stub for gossmap_chan_byidx */ +struct gossmap_chan *gossmap_chan_byidx(const struct gossmap *map UNNEEDED, u32 idx UNNEEDED) +{ fprintf(stderr, "gossmap_chan_byidx called!\n"); abort(); } +/* Generated stub for gossmap_chan_get_announce */ +u8 *gossmap_chan_get_announce(const tal_t *ctx UNNEEDED, + const struct gossmap *map UNNEEDED, + const struct gossmap_chan *c UNNEEDED) +{ fprintf(stderr, "gossmap_chan_get_announce called!\n"); abort(); } +/* Generated stub for gossmap_chan_get_update */ +u8 *gossmap_chan_get_update(const tal_t *ctx UNNEEDED, + const struct gossmap *map UNNEEDED, + const struct gossmap_chan *chan UNNEEDED, + int dir UNNEEDED) +{ fprintf(stderr, "gossmap_chan_get_update called!\n"); abort(); } +/* Generated stub for gossmap_chan_get_update_details */ +void gossmap_chan_get_update_details(const struct gossmap *map UNNEEDED, + const struct gossmap_chan *chan UNNEEDED, + int dir UNNEEDED, + u32 *timestamp UNNEEDED, + u8 *message_flags UNNEEDED, + u8 *channel_flags UNNEEDED, + u32 *fee_base_msat UNNEEDED, + u32 *fee_proportional_millionths UNNEEDED, + struct amount_msat *htlc_minimum_msat UNNEEDED, + struct amount_msat *htlc_maximum_msat UNNEEDED) +{ fprintf(stderr, "gossmap_chan_get_update_details called!\n"); abort(); } +/* Generated stub for gossmap_chan_scid */ +struct short_channel_id gossmap_chan_scid(const struct gossmap *map UNNEEDED, + const struct gossmap_chan *c UNNEEDED) +{ fprintf(stderr, "gossmap_chan_scid called!\n"); abort(); } +/* Generated stub for gossmap_find_chan */ +struct gossmap_chan *gossmap_find_chan(const struct gossmap *map UNNEEDED, + const struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "gossmap_find_chan called!\n"); abort(); } +/* Generated stub for gossmap_find_node */ +struct gossmap_node *gossmap_find_node(const struct gossmap *map UNNEEDED, + const struct node_id *id UNNEEDED) +{ fprintf(stderr, "gossmap_find_node called!\n"); abort(); } +/* Generated stub for gossmap_manage_get_gossmap */ +struct gossmap *gossmap_manage_get_gossmap(struct gossmap_manage *gm UNNEEDED) +{ fprintf(stderr, "gossmap_manage_get_gossmap called!\n"); abort(); } +/* Generated stub for gossmap_max_chan_idx */ +u32 gossmap_max_chan_idx(const struct gossmap *map UNNEEDED) +{ fprintf(stderr, "gossmap_max_chan_idx called!\n"); abort(); } +/* Generated stub for gossmap_node_get_announce */ +u8 *gossmap_node_get_announce(const tal_t *ctx UNNEEDED, + const struct gossmap *map UNNEEDED, + const struct gossmap_node *n UNNEEDED) +{ fprintf(stderr, "gossmap_node_get_announce called!\n"); abort(); } +/* Generated stub for gossmap_node_get_id */ +void gossmap_node_get_id(const struct gossmap *map UNNEEDED, + const struct gossmap_node *node UNNEEDED, + struct node_id *id UNNEEDED) +{ fprintf(stderr, "gossmap_node_get_id called!\n"); abort(); } +/* Generated stub for gossmap_nth_node */ +struct gossmap_node *gossmap_nth_node(const struct gossmap *map UNNEEDED, + const struct gossmap_chan *chan UNNEEDED, + int n UNNEEDED) +{ fprintf(stderr, "gossmap_nth_node called!\n"); abort(); } /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } @@ -70,12 +120,10 @@ void peer_supplied_good_gossip(struct daemon *daemon UNNEEDED, const struct node_id *source_peer UNNEEDED, size_t amount UNNEEDED) { fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } -/* Generated stub for queue_peer_from_store */ -void queue_peer_from_store(struct peer *peer UNNEEDED, - const struct broadcastable *bcast UNNEEDED) -{ fprintf(stderr, "queue_peer_from_store called!\n"); abort(); } /* Generated stub for queue_peer_msg */ -void queue_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) +void queue_peer_msg(struct daemon *daemon UNNEEDED, + const struct node_id *peer UNNEEDED, + const u8 *msg TAKES UNNEEDED) { fprintf(stderr, "queue_peer_msg called!\n"); abort(); } /* Generated stub for status_fmt */ void status_fmt(enum log_level level UNNEEDED, diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c index b38d990d753d..4b14e108012c 100644 --- a/gossipd/test/run-extended-info.c +++ b/gossipd/test/run-extended-info.c @@ -52,15 +52,65 @@ struct peer *first_random_peer(struct daemon *daemon UNNEEDED, /* Generated stub for fromwire_gossipd_dev_set_max_scids_encode_size */ bool fromwire_gossipd_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED) { fprintf(stderr, "fromwire_gossipd_dev_set_max_scids_encode_size called!\n"); abort(); } -/* Generated stub for get_node */ -struct node *get_node(struct routing_state *rstate UNNEEDED, - const struct node_id *id UNNEEDED) -{ fprintf(stderr, "get_node called!\n"); abort(); } -/* Generated stub for gossip_store_get */ -const u8 *gossip_store_get(const tal_t *ctx UNNEEDED, - struct gossip_store *gs UNNEEDED, - u64 offset UNNEEDED) -{ fprintf(stderr, "gossip_store_get called!\n"); abort(); } +/* Generated stub for gossmap_chan_byidx */ +struct gossmap_chan *gossmap_chan_byidx(const struct gossmap *map UNNEEDED, u32 idx UNNEEDED) +{ fprintf(stderr, "gossmap_chan_byidx called!\n"); abort(); } +/* Generated stub for gossmap_chan_get_announce */ +u8 *gossmap_chan_get_announce(const tal_t *ctx UNNEEDED, + const struct gossmap *map UNNEEDED, + const struct gossmap_chan *c UNNEEDED) +{ fprintf(stderr, "gossmap_chan_get_announce called!\n"); abort(); } +/* Generated stub for gossmap_chan_get_update */ +u8 *gossmap_chan_get_update(const tal_t *ctx UNNEEDED, + const struct gossmap *map UNNEEDED, + const struct gossmap_chan *chan UNNEEDED, + int dir UNNEEDED) +{ fprintf(stderr, "gossmap_chan_get_update called!\n"); abort(); } +/* Generated stub for gossmap_chan_get_update_details */ +void gossmap_chan_get_update_details(const struct gossmap *map UNNEEDED, + const struct gossmap_chan *chan UNNEEDED, + int dir UNNEEDED, + u32 *timestamp UNNEEDED, + u8 *message_flags UNNEEDED, + u8 *channel_flags UNNEEDED, + u32 *fee_base_msat UNNEEDED, + u32 *fee_proportional_millionths UNNEEDED, + struct amount_msat *htlc_minimum_msat UNNEEDED, + struct amount_msat *htlc_maximum_msat UNNEEDED) +{ fprintf(stderr, "gossmap_chan_get_update_details called!\n"); abort(); } +/* Generated stub for gossmap_chan_scid */ +struct short_channel_id gossmap_chan_scid(const struct gossmap *map UNNEEDED, + const struct gossmap_chan *c UNNEEDED) +{ fprintf(stderr, "gossmap_chan_scid called!\n"); abort(); } +/* Generated stub for gossmap_find_chan */ +struct gossmap_chan *gossmap_find_chan(const struct gossmap *map UNNEEDED, + const struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "gossmap_find_chan called!\n"); abort(); } +/* Generated stub for gossmap_find_node */ +struct gossmap_node *gossmap_find_node(const struct gossmap *map UNNEEDED, + const struct node_id *id UNNEEDED) +{ fprintf(stderr, "gossmap_find_node called!\n"); abort(); } +/* Generated stub for gossmap_manage_get_gossmap */ +struct gossmap *gossmap_manage_get_gossmap(struct gossmap_manage *gm UNNEEDED) +{ fprintf(stderr, "gossmap_manage_get_gossmap called!\n"); abort(); } +/* Generated stub for gossmap_max_chan_idx */ +u32 gossmap_max_chan_idx(const struct gossmap *map UNNEEDED) +{ fprintf(stderr, "gossmap_max_chan_idx called!\n"); abort(); } +/* Generated stub for gossmap_node_get_announce */ +u8 *gossmap_node_get_announce(const tal_t *ctx UNNEEDED, + const struct gossmap *map UNNEEDED, + const struct gossmap_node *n UNNEEDED) +{ fprintf(stderr, "gossmap_node_get_announce called!\n"); abort(); } +/* Generated stub for gossmap_node_get_id */ +void gossmap_node_get_id(const struct gossmap *map UNNEEDED, + const struct gossmap_node *node UNNEEDED, + struct node_id *id UNNEEDED) +{ fprintf(stderr, "gossmap_node_get_id called!\n"); abort(); } +/* Generated stub for gossmap_nth_node */ +struct gossmap_node *gossmap_nth_node(const struct gossmap *map UNNEEDED, + const struct gossmap_chan *chan UNNEEDED, + int n UNNEEDED) +{ fprintf(stderr, "gossmap_nth_node called!\n"); abort(); } /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } @@ -74,12 +124,10 @@ void peer_supplied_good_gossip(struct daemon *daemon UNNEEDED, const struct node_id *source_peer UNNEEDED, size_t amount UNNEEDED) { fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } -/* Generated stub for queue_peer_from_store */ -void queue_peer_from_store(struct peer *peer UNNEEDED, - const struct broadcastable *bcast UNNEEDED) -{ fprintf(stderr, "queue_peer_from_store called!\n"); abort(); } /* Generated stub for queue_peer_msg */ -void queue_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) +void queue_peer_msg(struct daemon *daemon UNNEEDED, + const struct node_id *peer UNNEEDED, + const u8 *msg TAKES UNNEEDED) { fprintf(stderr, "queue_peer_msg called!\n"); abort(); } /* Generated stub for towire_warningfmt */ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, diff --git a/gossipd/test/run-next_block_range.c b/gossipd/test/run-next_block_range.c index dfebada2e481..4e38a60b9f02 100644 --- a/gossipd/test/run-next_block_range.c +++ b/gossipd/test/run-next_block_range.c @@ -26,10 +26,53 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, const struct sha256 *h UNNEEDED, struct pubkey *next UNNEEDED) { fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } +/* Generated stub for find_peer */ +struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "find_peer called!\n"); abort(); } /* Generated stub for first_random_peer */ struct peer *first_random_peer(struct daemon *daemon UNNEEDED, struct peer_node_id_map_iter *it UNNEEDED) { fprintf(stderr, "first_random_peer called!\n"); abort(); } +/* Generated stub for gossmap_chan_get_update_details */ +void gossmap_chan_get_update_details(const struct gossmap *map UNNEEDED, + const struct gossmap_chan *chan UNNEEDED, + int dir UNNEEDED, + u32 *timestamp UNNEEDED, + u8 *message_flags UNNEEDED, + u8 *channel_flags UNNEEDED, + u32 *fee_base_msat UNNEEDED, + u32 *fee_proportional_millionths UNNEEDED, + struct amount_msat *htlc_minimum_msat UNNEEDED, + struct amount_msat *htlc_maximum_msat UNNEEDED) +{ fprintf(stderr, "gossmap_chan_get_update_details called!\n"); abort(); } +/* Generated stub for gossmap_chan_scid */ +struct short_channel_id gossmap_chan_scid(const struct gossmap *map UNNEEDED, + const struct gossmap_chan *c UNNEEDED) +{ fprintf(stderr, "gossmap_chan_scid called!\n"); abort(); } +/* Generated stub for gossmap_find_chan */ +struct gossmap_chan *gossmap_find_chan(const struct gossmap *map UNNEEDED, + const struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "gossmap_find_chan called!\n"); abort(); } +/* Generated stub for gossmap_manage_get_gossmap */ +struct gossmap *gossmap_manage_get_gossmap(struct gossmap_manage *gm UNNEEDED) +{ fprintf(stderr, "gossmap_manage_get_gossmap called!\n"); abort(); } +/* Generated stub for gossmap_max_node_idx */ +u32 gossmap_max_node_idx(const struct gossmap *map UNNEEDED) +{ fprintf(stderr, "gossmap_max_node_idx called!\n"); abort(); } +/* Generated stub for gossmap_node_byidx */ +struct gossmap_node *gossmap_node_byidx(const struct gossmap *map UNNEEDED, u32 idx UNNEEDED) +{ fprintf(stderr, "gossmap_node_byidx called!\n"); abort(); } +/* Generated stub for gossmap_nth_chan */ +struct gossmap_chan *gossmap_nth_chan(const struct gossmap *map UNNEEDED, + const struct gossmap_node *node UNNEEDED, + u32 n UNNEEDED, + int *which_half UNNEEDED) +{ fprintf(stderr, "gossmap_nth_chan called!\n"); abort(); } +/* Generated stub for gossmap_nth_node */ +struct gossmap_node *gossmap_nth_node(const struct gossmap *map UNNEEDED, + const struct gossmap_chan *chan UNNEEDED, + int n UNNEEDED) +{ fprintf(stderr, "gossmap_nth_node called!\n"); abort(); } /* Generated stub for memleak_scan_intmap_ */ void memleak_scan_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) { fprintf(stderr, "memleak_scan_intmap_ called!\n"); abort(); } @@ -62,11 +105,10 @@ bool query_short_channel_ids(struct daemon *daemon UNNEEDED, void (*cb)(struct peer *peer_ UNNEEDED, bool complete)) { fprintf(stderr, "query_short_channel_ids called!\n"); abort(); } /* Generated stub for queue_peer_msg */ -void queue_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) +void queue_peer_msg(struct daemon *daemon UNNEEDED, + const struct node_id *peer UNNEEDED, + const u8 *msg TAKES UNNEEDED) { fprintf(stderr, "queue_peer_msg called!\n"); abort(); } -/* Generated stub for random_select */ -bool random_select(double weight UNNEEDED, double *tot_weight UNNEEDED) -{ fprintf(stderr, "random_select called!\n"); abort(); } /* Generated stub for status_failed */ void status_failed(enum status_failreason code UNNEEDED, const char *fmt UNNEEDED, ...) @@ -77,11 +119,6 @@ void status_fmt(enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "status_fmt called!\n"); abort(); } -/* Generated stub for would_ratelimit_cupdate */ -bool would_ratelimit_cupdate(struct routing_state *rstate UNNEEDED, - const struct half_chan *hc UNNEEDED, - u32 timestamp UNNEEDED) -{ fprintf(stderr, "would_ratelimit_cupdate called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ static void test_block_range(struct seeker *seeker, diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index d9047c994fda..522ef342afbc 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -1,12 +1,15 @@ #include "config.h" -#include "../routing.c" +#include "../txout_failures.c" #include "../common/timeout.c" #include #include +#include #include #include #include #include +#include +#include #include /* AUTOGENERATED MOCKS START */ @@ -25,84 +28,11 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, const struct sha256 *h UNNEEDED, struct pubkey *next UNNEEDED) { fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } -/* Generated stub for daemon_conn_send */ -void daemon_conn_send(struct daemon_conn *dc UNNEEDED, const u8 *msg UNNEEDED) -{ fprintf(stderr, "daemon_conn_send called!\n"); abort(); } -/* Generated stub for get_cupdate_parts */ -void get_cupdate_parts(const u8 *channel_update UNNEEDED, - const u8 *parts[2] UNNEEDED, - size_t sizes[2]) -{ fprintf(stderr, "get_cupdate_parts called!\n"); abort(); } -/* Generated stub for gossip_store_add */ -u64 gossip_store_add(struct gossip_store *gs UNNEEDED, const u8 *gossip_msg UNNEEDED, - u32 timestamp UNNEEDED, bool zombie UNNEEDED, bool spam UNNEEDED, bool dying UNNEEDED, - const u8 *addendum UNNEEDED) -{ fprintf(stderr, "gossip_store_add called!\n"); abort(); } -/* Generated stub for gossip_store_delete */ -void gossip_store_delete(struct gossip_store *gs UNNEEDED, - struct broadcastable *bcast UNNEEDED, - int type UNNEEDED) -{ fprintf(stderr, "gossip_store_delete called!\n"); abort(); } -/* Generated stub for gossip_store_get */ -const u8 *gossip_store_get(const tal_t *ctx UNNEEDED, - struct gossip_store *gs UNNEEDED, - u64 offset UNNEEDED) -{ fprintf(stderr, "gossip_store_get called!\n"); abort(); } -/* Generated stub for gossip_store_mark_channel_deleted */ -void gossip_store_mark_channel_deleted(struct gossip_store *gs UNNEEDED, - const struct short_channel_id *scid UNNEEDED) -{ fprintf(stderr, "gossip_store_mark_channel_deleted called!\n"); abort(); } -/* Generated stub for gossip_store_mark_dying */ -void gossip_store_mark_dying(struct gossip_store *gs UNNEEDED, - const struct broadcastable *bcast UNNEEDED, - int type UNNEEDED) -{ fprintf(stderr, "gossip_store_mark_dying called!\n"); abort(); } -/* Generated stub for memleak_add_helper_ */ -void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, - const tal_t *)){ } -/* Generated stub for memleak_scan_htable */ -void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_scan_htable called!\n"); abort(); } -/* Generated stub for memleak_scan_intmap_ */ -void memleak_scan_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) -{ fprintf(stderr, "memleak_scan_intmap_ called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } -/* Generated stub for peer_supplied_good_gossip */ -void peer_supplied_good_gossip(struct daemon *daemon UNNEEDED, - const struct node_id *source_peer UNNEEDED, - size_t amount UNNEEDED) -{ fprintf(stderr, "peer_supplied_good_gossip 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 status_fmt */ -void status_fmt(enum log_level level UNNEEDED, - const struct node_id *peer UNNEEDED, - const char *fmt UNNEEDED, ...) - -{ fprintf(stderr, "status_fmt called!\n"); abort(); } -/* Generated stub for towire_gossipd_remote_channel_update */ -u8 *towire_gossipd_remote_channel_update(const tal_t *ctx UNNEEDED, const struct node_id *source_node UNNEEDED, const struct peer_update *peer_update UNNEEDED) -{ fprintf(stderr, "towire_gossipd_remote_channel_update called!\n"); abort(); } -/* Generated stub for towire_warningfmt */ -u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, - const struct channel_id *channel UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "towire_warningfmt called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ -/* NOOP stub for gossip_store_new */ -struct gossip_store *gossip_store_new(struct routing_state *rstate UNNEEDED) -{ - return NULL; -} - int main(int argc, char *argv[]) { - struct routing_state *rstate; + struct txout_failures *txf; struct timer *t; struct short_channel_id scid1, scid2; struct daemon *daemon; @@ -111,22 +41,22 @@ int main(int argc, char *argv[]) daemon = tal(tmpctx, struct daemon); timers_init(&daemon->timers, time_mono()); - rstate = new_routing_state(tmpctx, daemon, NULL, false, false); + txf = txout_failures_new(tmpctx, daemon); scid1.u64 = 100; scid2.u64 = 200; - assert(!in_txout_failures(rstate, &scid1)); - assert(!in_txout_failures(rstate, &scid2)); + assert(!in_txout_failures(txf, scid1)); + assert(!in_txout_failures(txf, scid2)); - add_to_txout_failures(rstate, &scid1); - assert(in_txout_failures(rstate, &scid1)); - assert(!in_txout_failures(rstate, &scid2)); - assert(rstate->num_txout_failures == 1); + txout_failures_add(txf, scid1); + assert(in_txout_failures(txf, scid1)); + assert(!in_txout_failures(txf, scid2)); + assert(txf->num == 1); - add_to_txout_failures(rstate, &scid2); - assert(in_txout_failures(rstate, &scid1)); - assert(in_txout_failures(rstate, &scid2)); - assert(rstate->num_txout_failures == 2); + txout_failures_add(txf, scid2); + assert(in_txout_failures(txf, scid1)); + assert(in_txout_failures(txf, scid2)); + assert(txf->num == 2); /* Move time forward 1 hour. */ t = timers_expire(&daemon->timers, @@ -136,9 +66,9 @@ int main(int argc, char *argv[]) timer_expired(t); /* Still there, just old. Refresh scid1 */ - assert(rstate->num_txout_failures == 0); - assert(in_txout_failures(rstate, &scid1)); - assert(rstate->num_txout_failures == 1); + assert(txf->num == 0); + assert(in_txout_failures(txf, scid1)); + assert(txf->num == 1); t = timers_expire(&daemon->timers, timemono_add(time_mono(), @@ -146,12 +76,12 @@ int main(int argc, char *argv[]) assert(t); timer_expired(t); - assert(rstate->num_txout_failures == 0); - assert(in_txout_failures(rstate, &scid1)); - assert(rstate->num_txout_failures == 1); - assert(!in_txout_failures(rstate, &scid2)); + assert(txf->num == 0); + assert(in_txout_failures(txf, scid1)); + assert(txf->num == 1); + assert(!in_txout_failures(txf, scid2)); - tal_free(rstate); + tal_free(txf); timers_cleanup(&daemon->timers); common_shutdown(); diff --git a/gossipd/txout_failures.c b/gossipd/txout_failures.c new file mode 100644 index 000000000000..823f150e11a4 --- /dev/null +++ b/gossipd/txout_failures.c @@ -0,0 +1,59 @@ +#include "config.h" +#include +#include +#include + +/* Convenience to tell the two failure maps apart */ +#define RECENT 0 +#define AGED 1 + +#define MAX_ENTRIES 10000 + +/* Once an hour, or at 10000 entries, we expire old ones */ +static void txout_failures_age(struct txout_failures *txf) +{ + uintmap_clear(&txf->failures[AGED]); + txf->failures[AGED] = txf->failures[RECENT]; + uintmap_init(&txf->failures[RECENT]); + txf->num = 0; + + txf->age_timer = new_reltimer(&txf->daemon->timers, txf, + time_from_sec(3600), + txout_failures_age, txf); +} + +struct txout_failures *txout_failures_new(const tal_t *ctx, struct daemon *daemon) +{ + struct txout_failures *txf = tal(ctx, struct txout_failures); + + txf->daemon = daemon; + uintmap_init(&txf->failures[RECENT]); + uintmap_init(&txf->failures[AGED]); + + txout_failures_age(txf); + return txf; +} + +void txout_failures_add(struct txout_failures *txf, + const struct short_channel_id scid) +{ + if (uintmap_add(&txf->failures[RECENT], scid.u64, true) + && ++txf->num == MAX_ENTRIES) { + tal_free(txf->age_timer); + txout_failures_age(txf); + } +} + +bool in_txout_failures(struct txout_failures *txf, + const struct short_channel_id scid) +{ + if (uintmap_get(&txf->failures[RECENT], scid.u64)) + return true; + + /* If we were going to expire it, we no longer are. */ + if (uintmap_get(&txf->failures[AGED], scid.u64)) { + txout_failures_add(txf, scid); + return true; + } + return false; +} diff --git a/gossipd/txout_failures.h b/gossipd/txout_failures.h new file mode 100644 index 000000000000..6afcbc3bd571 --- /dev/null +++ b/gossipd/txout_failures.h @@ -0,0 +1,42 @@ +#ifndef LIGHTNING_GOSSIPD_TXOUT_FAILURES_H +#define LIGHTNING_GOSSIPD_TXOUT_FAILURES_H +#include "config.h" +#include + +/* Cache for txout queries that failed. Allows us to skip failed + * checks if we get another announcement for the same scid. */ +struct txout_failures { + /* Cache for txout queries that failed. Allows us to skip failed + * checks if we get another announcement for the same scid. */ + size_t num; + UINTMAP(bool) failures[2]; + struct oneshot *age_timer; + + /* For access to timers */ + struct daemon *daemon; +}; + +/** + * txout_failures_new: allocate a new failure set. + * @ctx: tal context + * @daemon: global daemon struct + */ +struct txout_failures *txout_failures_new(const tal_t *ctx, struct daemon *daemon); + +/** + * txout_failures_add: add a failed scid to the set. + * @txf: the struct txout_failures. + * @scid: the short_channel_id to add. + */ +void txout_failures_add(struct txout_failures *txf, + const struct short_channel_id scid); + +/** + * in_txout_failures: is this scid in the set? + * @txf: the struct txout_failures. + * @scid: the short_channel_id to test. + */ +bool in_txout_failures(struct txout_failures *txf, + const struct short_channel_id scid); + +#endif /* LIGHTNING_GOSSIPD_TXOUT_FAILURES_H */ diff --git a/lightningd/channel_gossip.c b/lightningd/channel_gossip.c index 97a9d81d73e8..a7f123d1f415 100644 --- a/lightningd/channel_gossip.c +++ b/lightningd/channel_gossip.c @@ -324,7 +324,7 @@ static void broadcast_public_cupdate(struct channel *channel, take(sign_update(NULL, channel->peer->ld, cupdate))); subd_req(ld->gossip, ld->gossip, - take(towire_gossipd_addgossip(NULL, cg->cupdate)), + take(towire_gossipd_addgossip(NULL, cg->cupdate, NULL)), -1, 0, broadcast_public_cupdate_addgossip_reply, channel); } @@ -496,7 +496,7 @@ static void send_channel_announcement(struct channel *channel) &cg->remote_sigs->bitcoin_sig); subd_req(ld->gossip, ld->gossip, - take(towire_gossipd_addgossip(NULL, ca)), + take(towire_gossipd_addgossip(NULL, ca, &channel->funding_sats)), -1, 0, send_channel_announce_addgossip_reply, channel); /* We can also send our first public channel_update now */ broadcast_public_cupdate(channel, true); @@ -1088,6 +1088,6 @@ void channel_gossip_node_announce(struct lightningd *ld) /* Tell gossipd. */ subd_req(ld->gossip, ld->gossip, - take(towire_gossipd_addgossip(NULL, nannounce)), + take(towire_gossipd_addgossip(NULL, nannounce, NULL)), -1, 0, node_announce_addgossip_reply, NULL); } diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index db3d8867e581..37bdb48df4f7 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -174,7 +174,6 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_OUTPOINTS_SPENT: case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: case WIRE_GOSSIPD_DEV_MEMLEAK: - case WIRE_GOSSIPD_DEV_COMPACT_STORE: case WIRE_GOSSIPD_DEV_SET_TIME: case WIRE_GOSSIPD_NEW_BLOCKHEIGHT: case WIRE_GOSSIPD_ADDGOSSIP: @@ -182,7 +181,6 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) /* This is a reply, so never gets through to here. */ case WIRE_GOSSIPD_INIT_REPLY: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: - case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY: case WIRE_GOSSIPD_ADDGOSSIP_REPLY: case WIRE_GOSSIPD_NEW_BLOCKHEIGHT_REPLY: case WIRE_GOSSIPD_GET_ADDRS_REPLY: @@ -412,7 +410,7 @@ static struct command_result *json_addgossip(struct command *cmd, NULL)) return command_param_failed(); - req = towire_gossipd_addgossip(cmd, gossip_msg); + req = towire_gossipd_addgossip(cmd, gossip_msg, NULL); subd_req(cmd->ld->gossip, cmd->ld->gossip, req, -1, 0, json_addgossip_reply, cmd); @@ -456,50 +454,6 @@ static const struct json_command dev_set_max_scids_encode_size = { }; AUTODATA(json_command, &dev_set_max_scids_encode_size); -static void dev_compact_gossip_store_reply(struct subd *gossip UNUSED, - const u8 *reply, - const int *fds UNUSED, - struct command *cmd) -{ - bool success; - - if (!fromwire_gossipd_dev_compact_store_reply(reply, &success)) { - was_pending(command_fail(cmd, LIGHTNINGD, - "Gossip gave bad dev_gossip_compact_store_reply")); - return; - } - - if (!success) - was_pending(command_fail(cmd, LIGHTNINGD, - "gossip_compact_store failed")); - else - was_pending(command_success(cmd, json_stream_success(cmd))); -} - -static struct command_result *json_dev_compact_gossip_store(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) -{ - u8 *msg; - if (!param(cmd, buffer, params, NULL)) - return command_param_failed(); - - msg = towire_gossipd_dev_compact_store(NULL); - subd_req(cmd->ld->gossip, cmd->ld->gossip, - take(msg), -1, 0, dev_compact_gossip_store_reply, cmd); - return command_still_pending(cmd); -} - -static const struct json_command dev_compact_gossip_store = { - "dev-compact-gossip-store", - "developer", - json_dev_compact_gossip_store, - "Ask gossipd to rewrite the gossip store.", - .dev_only = true, -}; -AUTODATA(json_command, &dev_compact_gossip_store); - static struct command_result *json_dev_gossip_set_time(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, diff --git a/plugins/topology.c b/plugins/topology.c index dd934c02f386..f43688e713bb 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -264,9 +264,9 @@ static void json_add_halfchan(struct json_stream *response, json_add_node_id(response, "destination", &node_id[!dir]); json_add_short_channel_id(response, "short_channel_id", &scid); json_add_num(response, "direction", dir); - json_add_bool(response, "public", !c->private); + json_add_bool(response, "public", !gossmap_chan_is_localmod(gossmap, c)); - if (c->private) { + if (gossmap_chan_is_localmod(gossmap, c)) { /* Local additions don't have a channel_update * in gossmap. This is deprecated anyway, but * fill in values from entry we added. */ @@ -666,7 +666,7 @@ listpeerchannels_listincoming_done(struct command *cmd, json_add_u32(js, "cltv_expiry_delta", ourchan->half[!dir].delay); json_add_amount_msat(js, "incoming_capacity_msat", peer_capacity(gossmap, me, peer, ourchan)); - json_add_bool(js, "public", !ourchan->private); + json_add_bool(js, "public", !gossmap_chan_is_localmod(gossmap, ourchan)); peer_features = gossmap_node_get_features(tmpctx, gossmap, peer); if (peer_features) json_add_hex_talarr(js, "peer_features", peer_features); diff --git a/tests/test_closing.py b/tests/test_closing.py index aaabdc624453..90fb0a3a34a5 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -297,6 +297,12 @@ def test_closing_specified_destination(node_factory, bitcoind, chainparams): mine_funding_to_announce(bitcoind, [l1, l2, l3, l4]) + # Make sure they all see all the gossip before close, otherwise they might + # get upset with "bad gossip!" + for n in [l1, l2, l3, l4]: + wait_for(lambda: len(n.rpc.listchannels()['channels']) == 6) + wait_for(lambda: ['alias' in node for node in n.rpc.listnodes()['nodes']] == [True] * 4) + addr = chainparams['example_addr'] l1.rpc.close(chan12, None, addr) l1.rpc.call('close', {'id': chan13, 'destination': addr}) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 2079efd8b9fe..5c61bfa77c35 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1168,7 +1168,7 @@ def test_gossip_store_load(node_factory): l1.start() # May preceed the Started msg waited for in 'start'. - wait_for(lambda: l1.daemon.is_in_log(r'gossip_store: Read 1/1/1/0 cannounce/cupdate/nannounce/cdelete from store \(0 deleted\) in 778 bytes')) + wait_for(lambda: l1.daemon.is_in_log('Read 1/1/1/0 cannounce/cupdate/nannounce/delete from store in 800 bytes, now 778 bytes')) assert not l1.daemon.is_in_log('gossip_store.*truncating') @@ -1194,8 +1194,11 @@ def test_gossip_store_v10_upgrade(node_factory): "1ea7c2eadf8a29eb8690511a519b5656e29aa0a853771c4e38e65c5abf43d907295a915e69e451f4c7a0c3dc13dd943cfbe3ae88c0b96667cd7d58955dbfedcf43497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea33090000000013a63c0000b500015b8d9b440000009000000000000003e8000003e800000001")) l1.start() - # May preceed the Started msg waited for in 'start'. - wait_for(lambda: l1.daemon.is_in_log(r'gossip_store: Unupdated channel_announcement at 1.')) + # Channel "exists" but doesn't show in listchannels, as it has no updates. + assert l1.rpc.listchannels() == {'channels': []} + assert only_one(l1.rpc.listnodes('021bf3de4e84e3d52f9a3e36fbdcd2c4e8dbf203b9ce4fc07c2f03be6c21d0c675')['nodes']) + assert only_one(l1.rpc.listnodes('03f113414ebdc6c1fb0f33c99cd5a1d09dd79e7fdf2468cf1fe1af6674361695d2')['nodes']) + assert len(l1.rpc.listnodes()['nodes']) == 2 def test_gossip_store_load_announce_before_update(node_factory): @@ -1233,14 +1236,9 @@ def test_gossip_store_load_announce_before_update(node_factory): l1.start() # May preceed the Started msg waited for in 'start'. - wait_for(lambda: l1.daemon.is_in_log(r'gossip_store: Read 1/1/1/0 cannounce/cupdate/nannounce/cdelete from store \(0 deleted\) in 778 bytes')) + wait_for(lambda: l1.daemon.is_in_log('Read 1/1/1/1 cannounce/cupdate/nannounce/delete from store in 950 bytes, now 778 bytes')) assert not l1.daemon.is_in_log('gossip_store.*truncating') - # Extra sanity check if we can. - l1.rpc.call('dev-compact-gossip-store') - l1.restart() - l1.rpc.call('dev-compact-gossip-store') - def test_gossip_store_load_amount_truncated(node_factory): """Make sure we can read canned gossip store with truncated amount""" @@ -1255,15 +1253,10 @@ def test_gossip_store_load_amount_truncated(node_factory): l1.start() # May preceed the Started msg waited for in 'start'. - wait_for(lambda: l1.daemon.is_in_log(r'gossip_store: dangling channel_announcement. Moving to gossip_store.corrupt and truncating')) - wait_for(lambda: l1.daemon.is_in_log(r'gossip_store: Read 0/0/0/0 cannounce/cupdate/nannounce/cdelete from store \(0 deleted\) in 1 bytes')) + wait_for(lambda: l1.daemon.is_in_log(r'\*\*BROKEN\*\* gossipd: gossip_store: channel_announcement without amount \(offset 1\). Moving to gossip_store.corrupt and truncating')) + wait_for(lambda: l1.daemon.is_in_log(r'gossip_store: Read 0/0/0/0 cannounce/cupdate/nannounce/delete from store in 467 bytes, now 1 bytes \(populated=false\)')) assert os.path.exists(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store.corrupt')) - # Extra sanity check if we can. - l1.rpc.call('dev-compact-gossip-store') - l1.restart() - l1.rpc.call('dev-compact-gossip-store') - @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') @@ -1582,7 +1575,6 @@ def test_gossip_store_compact_noappend(node_factory, bitcoind): with open(os.path.join(l2.daemon.lightning_dir, TEST_NETWORK, 'gossip_store.tmp'), 'wb') as f: f.write(bytearray.fromhex("07deadbeef")) - l2.rpc.call('dev-compact-gossip-store') l2.restart() wait_for(lambda: l2.daemon.is_in_log('gossip_store: Read ')) assert not l2.daemon.is_in_log('gossip_store:.*truncate') @@ -1596,32 +1588,6 @@ def test_gossip_store_load_complex(node_factory, bitcoind): wait_for(lambda: l2.daemon.is_in_log('gossip_store: Read ')) -def test_gossip_store_compact(node_factory, bitcoind): - l2 = setup_gossip_store_test(node_factory, bitcoind) - - # Now compact store. - l2.rpc.call('dev-compact-gossip-store') - - # Should still be connected. - time.sleep(1) - assert len(l2.rpc.listpeers()['peers']) == 2 - - # Should restart ok. - l2.restart() - wait_for(lambda: l2.daemon.is_in_log('gossip_store: Read ')) - - -def test_gossip_store_compact_restart(node_factory, bitcoind): - l2 = setup_gossip_store_test(node_factory, bitcoind) - - # Should restart ok. - l2.restart() - wait_for(lambda: l2.daemon.is_in_log('gossip_store: Read ')) - - # Now compact store. - l2.rpc.call('dev-compact-gossip-store') - - def test_gossip_store_load_no_channel_update(node_factory): """Make sure we can read truncated gossip store with a channel_announcement and no channel_update""" l1 = node_factory.get_node(start=False, allow_broken_log=True) @@ -1648,14 +1614,8 @@ def test_gossip_store_load_no_channel_update(node_factory): l1.start() # May preceed the Started msg waited for in 'start'. - wait_for(lambda: l1.daemon.is_in_log('gossip_store: Unupdated channel_announcement at 1. Moving to gossip_store.corrupt and truncating')) - assert os.path.exists(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store.corrupt')) - - # This should actually result in an empty store. - l1.rpc.call('dev-compact-gossip-store') - - with open(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store'), "rb") as f: - assert bytearray(f.read()) == bytearray.fromhex("0d") + wait_for(lambda: l1.daemon.is_in_log('Read 1/0/1/0 cannounce/cupdate/nannounce/delete from store in 650 bytes, now 628 bytes')) + assert not os.path.exists(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store.corrupt')) def test_gossip_store_compact_on_load(node_factory, bitcoind): @@ -1669,9 +1629,7 @@ def test_gossip_store_compact_on_load(node_factory, bitcoind): l2.restart() # These appear before we're fully started, so will already in log: - l2.daemon.is_in_log(r'gossip_store_compact_offline: 2 deleted, 9 copied') - - assert l2.daemon.is_in_log(r'gossip_store: Read 2/4/3/0 cannounce/cupdate/nannounce/cdelete from store \(0 deleted\)') + assert l2.daemon.is_in_log('gossip_store: Read 2/4/3/2 cannounce/cupdate/nannounce/delete from store') def test_gossip_announce_invalid_block(node_factory, bitcoind): @@ -1749,132 +1707,6 @@ def test_gossip_no_backtalk(node_factory): TEST_NETWORK != 'regtest', "Channel announcement contains genesis hash, receiving node discards on mismatch" ) -def test_gossip_ratelimit(node_factory, bitcoind): - """Check that we ratelimit incoming gossip. - - We create a partitioned network, in which the first partition consisting - of l1 and l2 is used to create an on-chain footprint and we then feed - canned gossip to the other partition consisting of l3. l3 should ratelimit - the incoming gossip. - - We get BROKEN logs because gossipd talks about non-existent channels to - lightningd ("**BROKEN** lightningd: Local update for bad scid 103x1x1"). - """ - l3 = node_factory.get_node(node_id=3, - allow_broken_log=True, - options={'dev-gossip-time': 1568096251}) - - # Bump to block 102, so the following tx ends up in 103x1: - bitcoind.generate_block(1) - - # We don't actually need to start l1 and l2, they're just there to create - # an unspent outpoint matching the expected script. This is also more - # stable against output ordering issues. - tx = bitcoind.rpc.createrawtransaction( - [], - [ - # Fundrawtransaction will fill in the first output with the change - {"bcrt1qtwxd8wg5eanumk86vfeujvp48hfkgannf77evggzct048wggsrxsum2pmm": 0.01000000} - ] - ) - tx = bitcoind.rpc.fundrawtransaction(tx, {'changePosition': 0})['hex'] - tx = bitcoind.rpc.signrawtransactionwithwallet(tx)['hex'] - txid = bitcoind.rpc.sendrawtransaction(tx) - wait_for(lambda: txid in bitcoind.rpc.getrawmempool()) - - # Make the tx gossipable: - bitcoind.generate_block(6) - sync_blockheight(bitcoind, [l3, ]) - - def channel_fees(node): - channels = node.rpc.listchannels()['channels'] - return [c['fee_per_millionth'] for c in channels] - - # Here are some ones I generated earlier (by removing gossip - # ratelimiting) - subprocess.check_call( - [ - 'devtools/gossipwith', - '--max-messages=0', - '{}@localhost:{}'.format(l3.info['id'], l3.port), - # announcement - '0100987b271fc95a37dbed78e6159e0ab792cda64603780454ce80832b4e31f63a6760abc8fdc53be35bb7cfccd125ee3d15b4fbdfb42165098970c19c7822bb413f46390e0c043c777226927eacd2186a03f064e4bdc30f891cb6e4990af49967d34b338755e99d728987e3d49227815e17f3ab40092434a59e33548e870071176db7d44d8c8f4c4cac27ae6554eb9350e97d47617e3a1355296c78e8234446fa2f138ad1b03439f18520227fb9e9eb92689b3a0ed36e6764f5a41777e9a2a4ce1026d19a4e4d8f7715c13ac2d6bf3238608a1ccf9afd91f774d84d170d9edddebf7460c54d49bd6cd81410bc3eeeba2b7278b1b5f7e748d77d793f31086847d582000006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f0000670000010001022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d590266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c0351802e3bd38009866c9da8ec4aa99cc4ea9c6c0dd46df15c61ef0ce1f271291714e5702324266de8403b3ab157a09f1f784d587af61831c998c151bcc21bb74c2b2314b', - # first update is free - '010225bfd9c5e2c5660188a14deb4002cd645ee67f00ad3b82146e46711ec460cb0c6819fdd1c680cb6d24e3906679ef071f13243a04a123e4b83310ebf0518ffd4206226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d773ffb010100060000000000000000000000010000000a000000003b023380' - ], - timeout=TIMEOUT - ) - - # Wait for it to process channel. - wait_for(lambda: channel_fees(l3) == [10]) - - subprocess.check_call( - [ - 'devtools/gossipwith', - '--max-messages=0', - '{}@localhost:{}'.format(l3.info['id'], l3.port), - # next 4 are let through... - '01023a892ad9c9953a54ad3b8e2e03a93d1c973241b62f9a5cd1f17d5cdf08de0e8b4fcd24aa8bd45a48b788fe9dab3d416f28dfa390bc900ec0176ec5bd1afd435706226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77400001010006000000000000000000000014000003e9000000003b023380', - '010245966763623ebc16796165263d4b21711ef04ebf3929491e695ff89ed2b8ccc0668ceb9e35e0ff5b8901d95732a119c1ed84ac99861daa2de462118f7b70049f06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77400101010006000000000000000000000014000003ea000000003b023380', - '0102c479b7684b9db496b844f6925f4ffd8a27c5840a020d1b537623c1545dcd8e195776381bbf51213e541a853a4a49a0faf84316e7ccca5e7074901a96bbabe04e06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77400201010006000000000000000000000014000003eb000000003b023380', - # timestamp=1568096259, fee_proportional_millionths=1004 - '01024b866012d995d3d7aec7b7218a283de2d03492dbfa21e71dd546ec2e36c3d4200453420aa02f476f99c73fe1e223ea192f5fa544b70a8319f2a216f1513d503d06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77400301010006000000000000000000000014000003ec000000003b023380', - # update 5 marks you as a nasty spammer, but the routing graph is - # updated with this even though the gossip is not broadcast. - '01025b5b5a0daed874ab02bd3356d38190ff46bbaf5f10db5067da70f3ca203480ca78059e6621c6143f3da4e454d0adda6d01a9980ed48e71ccd0c613af73570a7106226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77400401010006000000000000000000000014000003ed000000003b023380' - ], - timeout=TIMEOUT - ) - # Rate limited channel_update okay to use in routing graph. - wait_for(lambda: channel_fees(l3) == [1005]) - # but should be flagged so we don't propagate to the network. - assert(l3.daemon.is_in_log("Spammy update for 103x1x1/1 flagged")) - - # ask for a gossip sync - raw = subprocess.run(['devtools/gossipwith', - '--initial-sync', - '--timeout-after={}'.format(1), - '--hex', - '{}@localhost:{}'.format(l3.info['id'], l3.port)], - check=True, - timeout=TIMEOUT, stdout=subprocess.PIPE).stdout - # The last message is the most recent channel update. - message = raw.decode('utf-8').split()[-1] - decoded = subprocess.run(['devtools/decodemsg', message], - check=True, - timeout=TIMEOUT, - stdout=subprocess.PIPE).stdout.decode('utf8') - assert("fee_proportional_millionths=1004" in decoded) - # Used in routing graph, but not passed to gossip peers. - assert("fee_proportional_millionths=1005" not in decoded) - - # 24 seconds later, it will accept another. - l3.rpc.call('dev-gossip-set-time', [1568096251 + 24]) - - subprocess.run(['devtools/gossipwith', - '--max-messages=0', - '{}@localhost:{}'.format(l3.info['id'], l3.port), - # update 6: timestamp=1568096284 fee_proportional_millionths=1006 - '010282d24bcd984956bd9b891848404ee59d89643923b21641d2c2c0770a51b8f5da00cef82458add970f0b654aa4c8d54f68a9a1cc6470a35810303b09437f1f73d06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77401c01010006000000000000000000000014000003ee000000003b023380'], - check=True, timeout=TIMEOUT) - - wait_for(lambda: channel_fees(l3) == [1006]) - raw = subprocess.run(['devtools/gossipwith', - '--initial-sync', - '--timeout-after={}'.format(1), - '--hex', - '{}@localhost:{}'.format(l3.info['id'], l3.port)], - check=True, - timeout=TIMEOUT, stdout=subprocess.PIPE).stdout - message = raw.decode('utf-8').split()[-1] - decoded = subprocess.run(['devtools/decodemsg', message], - check=True, - timeout=TIMEOUT, - stdout=subprocess.PIPE).stdout.decode('utf8') - - assert("fee_proportional_millionths=1006" in decoded) - - def check_socket(ip_addr, port): result = True sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -2133,74 +1965,6 @@ def get_gossip(node): assert len(get_gossip(l1)) == 2 -@pytest.mark.skip("Zombie research had unexpected side effects") -def test_channel_resurrection(node_factory, bitcoind): - """When a node goes offline long enough to prune a channel, the - channel_announcement should be retained in case the node comes back online. - """ - opts = {'dev-fast-gossip-prune': None, - 'may_reconnect': True} - l1, l2 = node_factory.get_nodes(2, opts=opts) - opts.update({'log-level': 'debug'}) - l3, = node_factory.get_nodes(1, opts=opts) - l1.rpc.connect(l2.info['id'], 'localhost', l2.port) - l3.rpc.connect(l2.info['id'], 'localhost', l2.port) - scid, _ = l1.fundchannel(l2, 10**6, True, True) - bitcoind.generate_block(6) - sync_blockheight(bitcoind, [l1, l2, l3]) - l3.wait_channel_active(scid) - start_time = int(time.time()) - # Channel_update should now be refreshed. - refresh_due = start_time + 44 - prune_due = start_time + 61 - l2.rpc.call('dev-gossip-set-time', [refresh_due]) - l3.rpc.call('dev-gossip-set-time', [refresh_due]) - # Automatic reconnect is too fast, so shutdown l1 instead of disconnecting - l1.stop() - l2.daemon.wait_for_log('Sending keepalive channel_update') - l3.daemon.wait_for_log('Received channel_update for channel 103x1') - # Wait for the next pruning cycle - l2.rpc.call('dev-gossip-set-time', [prune_due]) - l3.rpc.call('dev-gossip-set-time', [prune_due]) - # Make sure l1 is recognized as disconnected - wait_for(lambda: only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['connected'] is False) - # Wait for the channel to be pruned. - l3.daemon.wait_for_log("Pruning channel") - assert l3.rpc.listchannels()['channels'] == [] - l1.start() - time.sleep(1) - l1.rpc.call('dev-gossip-set-time', [prune_due]) - time.sleep(1) - l1.rpc.call('dev-gossip-set-time', [prune_due]) - wait_for(lambda: [c['active'] for c in l2.rpc.listchannels()['channels']] == [True, True]) - l1.rpc.call('dev-gossip-set-time', [prune_due + 30]) - l2.rpc.call('dev-gossip-set-time', [prune_due + 30]) - l3.rpc.call('dev-gossip-set-time', [prune_due + 30]) - # l2 should recognize its own channel as announceable - wait_for(lambda: [[c['public'], c['active']] for c in l2.rpc.listchannels()['channels']] == [[True, True], [True, True]], timeout=30) - # l3 should be able to recover the zombie channel - wait_for(lambda: [c['active'] for c in l3.rpc.listchannels()['channels']] == [True, True], timeout=30) - - # Now test spending the outpoint and removing a zombie channel from the store. - l2.stop() - prune_again = prune_due + 91 - l1.rpc.call('dev-gossip-set-time', [prune_again]) - l3.rpc.call('dev-gossip-set-time', [prune_again]) - l3.daemon.wait_for_log("Pruning channel") - txid = l1.rpc.close(l2.info['id'], 1)['txid'] - bitcoind.generate_block(13, txid) - l3.daemon.wait_for_log(f"Deleting channel {scid} due to the funding " - "outpoint being spent", 30) - # gossip_store is cleaned of zombie channels once outpoint is spent. - gs_path = os.path.join(l3.daemon.lightning_dir, TEST_NETWORK, 'gossip_store') - gs = subprocess.run(['devtools/dump-gossipstore', '--print-deleted', gs_path], - check=True, timeout=TIMEOUT, stdout=subprocess.PIPE) - print(gs.stdout.decode()) - for l in gs.stdout.decode().splitlines(): - if "ZOMBIE" in l: - assert ("DELETED" in l) - - def test_dump_own_gossip(node_factory): """We *should* send all self-related gossip unsolicited, if we have any""" l1, l2 = node_factory.line_graph(2, wait_for_announce=True) @@ -2238,98 +2002,6 @@ def test_dump_own_gossip(node_factory): assert expect == [] -@unittest.skipIf( - TEST_NETWORK != 'regtest', - "Channel announcement contains genesis hash, receiving node discards on mismatch" -) -def test_read_spam_nannounce(node_factory, bitcoind): - """issue #6531 lead to a node announcement not being deleted from - the gossip_store.""" - """broadcastable and spam node announcements should be loaded properly when - reading the gossip_store - even when they are both pending a channel - update.""" - opts = {'dev-gossip-time': 1691773540} - l1 = node_factory.get_node(start=False, opts=opts) - canned_store = ( - "0c" # Gossip store version byte - "0000" # length flags - "01b0" # length - "d163af25" # checksum - "64d66a3a" # timestamp - # Channel announcement - "010000335733f5942df5d950eb8766dee3a9d6626922844ed6ae7a110dd7e7edc32e3f6f3d9ac5cdea23ce25bb8dbf761fd3d5fc56c05b6856316d12e9d32ca0f08c69ca0306fe716e7b5151317d6440b7373d9fbc646ead48f2163f2b6d511afe6a79c75551c2620fc80974f2f864329d9778a08cdfbc9f2c9c1344c432702c66807cfb4db69b80fae8c33c70143d948b36614d620891bee2df99c86bc62207c17e3b9186214c0ccff2ded5598accc90eb1d5b2f7a83cd7f68d712ea047d8019f343063b0a236356a387146f58fa832ddc13c4714522cbb503f5f3ca8fcec6602be2438ad3f98fa0ceed58fe3a066d385fcacd98c704b2a8e98a3e20bf76b35b736000006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f0000670000010000022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d029053521d6ea7a52cdd55f733d0fb2d077c0373b0053b5b810d927244061b757302d6063d022691b2490ab454dee73a57c6ff5d308352b461ece69f3c284f2c2412" - # Channel Amount - "0000000a911183f600000000" - "100500000000000f4240" - # broadcastable node announcement (rgb=000001) - "0000009533d9cf8c64d66a44" - "010108f4e25debdd74d0f52b7f1da5dbd82f429d057f48e3d2ed49fcc65cfe3e185c086c1a83d8f3bb15dc0cc852d80390c24cd1fe6d288b91eb55cf98c4a9baf7c0000788a0000a0269a264d66a44022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d5900000153494c454e544152544953542d3536392d67303030643963302d6d6f646465640000" - # Rate-limited node announcment (rgb=000002) - "2000009519aa897a64d66a49" - "0101685f67556cd0a87e04c6d1e9daa4e31dcf14f881d6f1231b1bee8adf6666977b6b9baa497e91d2b6daae726cde69e3faf924d9c95d0ca6b374d6693d4fc0d648000788a0000a0269a264d66a49022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d5900000253494c454e544152544953542d3536392d67303030643963302d6d6f646465640000" - # Channel Update - "0000008a0234021c64d66a61" - "010242ce9d9e79f939399ea1291c04fffcafdfa911246464a4b48c16b7b816dd57b4168562a6c519eb31c37718b68bdfc6345d7c2db663b8b04a3558ce7736c5b61706226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f000067000001000064d66a6101000006000000000000000000000015000003e8000000003b023380" - # Channel Update - "0000008acd55fcb264d66a61" - "01023f5dd1f69f675a71d2a7a34956b26f12c4fe9ee287a8449a3eb7b756c583e3bb1334a8eb7c3e148d0f43e08b95c50017ba62da9a7843fe4850a3cb3c74dc5e2c06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f000067000001000064d66a6101010006000000000000000000000015000003e8000000003b023380" - ) - with open(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store'), 'wb') as f: - f.write(bytearray.fromhex(canned_store)) - - bitcoind.generate_block(1) - tx = bitcoind.rpc.createrawtransaction( - [], - [ - # Fundrawtransaction will fill in the first output with the change - {"bcrt1qpd7nwe3jrt07st89uy82nn2xrmqtxyzqpty5ygt6w546lf6n0wcskswjvh": 0.01000000} - ] - ) - tx = bitcoind.rpc.fundrawtransaction(tx, {'changePosition': 0})['hex'] - tx = bitcoind.rpc.signrawtransactionwithwallet(tx)['hex'] - txid = bitcoind.rpc.sendrawtransaction(tx) - wait_for(lambda: txid in bitcoind.rpc.getrawmempool()) - bitcoind.generate_block(6) - l1.start() - # retrieves node info originating from the spam announcement - node_info = l1.rpc.listnodes('022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59') - assert only_one(node_info['nodes'])['color'] == '000002' - - out = subprocess.run(['devtools/gossipwith', - '--initial-sync', - '--timeout-after={}'.format(int(math.sqrt(TIMEOUT) + 1)), - '--hex', - '{}@localhost:{}'.format(l1.info['id'], l1.port)], - check=True, - timeout=TIMEOUT, stdout=subprocess.PIPE).stdout.decode() - - received_broadcastable = False - received_spam = False - for message in out.splitlines(): - gos = subprocess.run(['devtools/decodemsg', message], check=True, - timeout=TIMEOUT, - stdout=subprocess.PIPE).stdout.decode() - - for line in gos.splitlines(): - if 'rgb_color=[000001]' in line: - received_broadcastable = True - if 'rgb_color=[000002]' in line: - received_spam = True - - assert received_broadcastable - assert not received_spam - # Send a new node announcement. It should replace both. - subprocess.run(['devtools/gossipwith', - '--max-messages=0', - '{}@localhost:{}'.format(l1.info['id'], l1.port), - # color=000003 - '0101273fd2c58deb4c3bd98610079657219a5c8291d6a85c3607eae895f25e08babd6e45edd1e62f719b20526ed1c8fc3c7d9e7e3fafa4f24e4cb64872d041a13503000788a0000a0269a264d66a4e022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d5900000353494c454e544152544953542d3536392d67303030643963302d6d6f646465640000'], - check=True, timeout=TIMEOUT) - l1.daemon.wait_for_log('Received node_announcement') - l1.restart() - assert not l1.daemon.is_in_log('BROKEN') - - def test_listchannels_deprecated_local(node_factory, bitcoind): """Test listchannels shows local/private channels only in deprecated mode""" l1, l2, l3 = node_factory.get_nodes(3, diff --git a/tests/test_renepay.py b/tests/test_renepay.py index 45caa1ec06dc..4d606bd9a01e 100644 --- a/tests/test_renepay.py +++ b/tests/test_renepay.py @@ -297,6 +297,10 @@ def start_channels(connections): scid = src.get_channel_scid(dst) scids.append(scid) + # Make sure they have all seen block so they don't complain about + # the coming gossip messages + sync_blockheight(bitcoind, nodes) + bitcoind.generate_block(5) # Make sure everyone sees all channels, all other nodes diff --git a/tools/bench-gossipd.sh b/tools/bench-gossipd.sh index cd9ba96f1af6..79540f1fcccf 100755 --- a/tools/bench-gossipd.sh +++ b/tools/bench-gossipd.sh @@ -5,7 +5,7 @@ set -e DIR="" TARGETS="" -DEFAULT_TARGETS=" store_load_msec vsz_kb store_rewrite_sec listnodes_sec listchannels_sec routing_sec peer_write_all_sec peer_read_all_sec " +DEFAULT_TARGETS=" store_load_msec vsz_kb listnodes_sec listchannels_sec routing_sec peer_write_all_sec peer_read_all_sec " MCP_DIR=../million-channels-project/data/1M/gossip/ CSV=false @@ -111,12 +111,6 @@ if [ -z "${TARGETS##* vsz_kb *}" ]; then ps -o vsz= -p "$(pidof lightning_gossipd)" | print_stat vsz_kb fi -# How long does rewriting the store take? -if [ -z "${TARGETS##* store_rewrite_sec *}" ]; then - # shellcheck disable=SC2086 - /usr/bin/time --append -f %e $LCLI1 dev-compact-gossip-store 2>&1 > /dev/null | print_stat store_rewrite_sec -fi - # Now, how long does listnodes take? if [ -z "${TARGETS##* listnodes_sec *}" ]; then # shellcheck disable=SC2086 diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 04160f03b1c9..863c5678906b 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1063,7 +1063,7 @@ u8 *towire_final_incorrect_cltv_expiry(const tal_t *ctx UNNEEDED, u32 cltv_expir u8 *towire_final_incorrect_htlc_amount(const tal_t *ctx UNNEEDED, struct amount_msat incoming_htlc_amt UNNEEDED) { fprintf(stderr, "towire_final_incorrect_htlc_amount called!\n"); abort(); } /* Generated stub for towire_gossipd_addgossip */ -u8 *towire_gossipd_addgossip(const tal_t *ctx UNNEEDED, const u8 *msg UNNEEDED) +u8 *towire_gossipd_addgossip(const tal_t *ctx UNNEEDED, const u8 *msg UNNEEDED, struct amount_sat *known_channel UNNEEDED) { fprintf(stderr, "towire_gossipd_addgossip called!\n"); abort(); } /* Generated stub for towire_hsmd_check_pubkey */ u8 *towire_hsmd_check_pubkey(const tal_t *ctx UNNEEDED, u32 index UNNEEDED, const struct pubkey *pubkey UNNEEDED)