From 55f5a5cabab415cc41441e065114621626a436b1 Mon Sep 17 00:00:00 2001 From: niftynei Date: Fri, 28 May 2021 15:12:41 -0500 Subject: [PATCH] gossip/liquidity-ad: node_ann comparison, optional TLV check Correctly mark whether or not the TLV's are the same/different, given two node announcements --- gossipd/gossip_generation.c | 56 +++++-- gossipd/gossip_generation.h | 3 +- gossipd/routing.c | 5 +- gossipd/test/run-check_channel_announcement.c | 3 +- gossipd/test/run-check_node_announcement.c | 147 ++++++++++++++++++ gossipd/test/run-txout_failure.c | 3 +- 6 files changed, 202 insertions(+), 15 deletions(-) create mode 100644 gossipd/test/run-check_node_announcement.c diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index d8e199157..a1fcd099d 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -96,13 +96,14 @@ bool cupdate_different(struct gossip_store *gs, || !memeq(oparts[1], osizes[1], nparts[1], nsizes[1]); } -/* Get non-signature, non-timestamp parts of (valid!) node_announcement */ +/* 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[2], - size_t sizes[2]) + const u8 *parts[3], + size_t sizes[3]) { - size_t len; - const u8 *flen; + size_t len, ad_len; + const u8 *flen, *ad_start; /* BOLT #7: * @@ -125,17 +126,43 @@ static void get_nannounce_parts(const u8 *node_announcement, 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; - sizes[1] = tal_count(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)? */ bool nannounce_different(struct gossip_store *gs, const struct node *node, - const u8 *nannounce) + const u8 *nannounce, + bool *only_missing_tlv) { - const u8 *oparts[2], *nparts[2]; - size_t osizes[2], nsizes[2]; + const u8 *oparts[3], *nparts[3]; + size_t osizes[3], nsizes[3]; const u8 *orig; /* Get last one we have. */ @@ -143,8 +170,14 @@ bool nannounce_different(struct gossip_store *gs, get_nannounce_parts(orig, oparts, osizes); get_nannounce_parts(nannounce, nparts, nsizes); + if (only_missing_tlv) + *only_missing_tlv = 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]); + return !memeq(oparts[0], osizes[0], nparts[0], nsizes[0]) - || !memeq(oparts[1], osizes[1], nparts[1], nsizes[1]); + || !memeq(oparts[1], osizes[1], nparts[1], nsizes[1]) + || !memeq(oparts[2], osizes[2], nparts[2], nsizes[2]); } /* This routine created a `node_announcement` for our node, and hands it to @@ -177,7 +210,8 @@ static void update_own_node_announcement(struct daemon *daemon) if (self && self->bcast.index) { u32 next; - if (!nannounce_different(daemon->rstate->gs, self, nannounce)) + if (!nannounce_different(daemon->rstate->gs, self, nannounce, + NULL)) return; /* BOLT #7: diff --git a/gossipd/gossip_generation.h b/gossipd/gossip_generation.h index 5c4857661..b1277ed0d 100644 --- a/gossipd/gossip_generation.h +++ b/gossipd/gossip_generation.h @@ -29,7 +29,8 @@ bool cupdate_different(struct gossip_store *gs, * node->bcast.index must be non-zero! */ bool nannounce_different(struct gossip_store *gs, const struct node *node, - const u8 *nannounce); + const u8 *nannounce, + bool *only_missing_tlv); /* Should we announce our own node? Called at strategic places. */ void maybe_send_own_node_announce(struct daemon *daemon); diff --git a/gossipd/routing.c b/gossipd/routing.c index 0fd8c0815..54b70312c 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -1656,6 +1656,8 @@ bool routing_add_node_announcement(struct routing_state *rstate, } if (node->bcast.index) { + bool only_tlv_diff; + if (index != 0) { status_broken("gossip_store node_announcement %u replaces %u!", index, node->bcast.index); @@ -1670,7 +1672,8 @@ bool routing_add_node_announcement(struct routing_state *rstate, /* Allow redundant updates once every 7 days */ if (timestamp < node->bcast.timestamp + GOSSIP_PRUNE_INTERVAL(rstate->dev_fast_gossip_prune) / 2 - && !nannounce_different(rstate->gs, node, msg)) { + && !nannounce_different(rstate->gs, node, msg, + &only_tlv_diff)) { SUPERVERBOSE( "Ignoring redundant nannounce for %s" " (last %u, now %u)", diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index caaea2ca3..411ae4c60 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -103,7 +103,8 @@ void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intma /* Generated stub for nannounce_different */ bool nannounce_different(struct gossip_store *gs UNNEEDED, const struct node *node UNNEEDED, - const u8 *nannounce UNNEEDED) + const u8 *nannounce UNNEEDED, + bool *only_missing_tlv UNNEEDED) { fprintf(stderr, "nannounce_different called!\n"); abort(); } /* Generated stub for new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c new file mode 100644 index 000000000..5017ce3d1 --- /dev/null +++ b/gossipd/test/run-check_node_announcement.c @@ -0,0 +1,147 @@ +#include "../gossip_generation.c" +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* 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 fmt_wireaddr_without_port */ +char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) +{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_local_channel_update */ +bool fromwire_gossipd_local_channel_update(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, bool *disable UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_local_channel_update called!\n"); abort(); } +/* Generated stub for fromwire_hsmd_cupdate_sig_reply */ +bool fromwire_hsmd_cupdate_sig_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **cu UNNEEDED) +{ fprintf(stderr, "fromwire_hsmd_cupdate_sig_reply called!\n"); abort(); } +/* Generated stub for fromwire_hsmd_node_announcement_sig_reply */ +bool fromwire_hsmd_node_announcement_sig_reply(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "fromwire_hsmd_node_announcement_sig_reply 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_time_now */ +struct timeabs gossip_time_now(const struct routing_state *rstate UNNEEDED) +{ fprintf(stderr, "gossip_time_now called!\n"); abort(); } +/* Generated stub for handle_channel_update */ +u8 *handle_channel_update(struct routing_state *rstate UNNEEDED, const u8 *update TAKES UNNEEDED, + struct peer *peer UNNEEDED, + struct short_channel_id *unknown_scid UNNEEDED, + bool force UNNEEDED) +{ fprintf(stderr, "handle_channel_update called!\n"); abort(); } +/* Generated stub for handle_node_announcement */ +u8 *handle_node_announcement(struct routing_state *rstate UNNEEDED, const u8 *node UNNEEDED, + struct peer *peer UNNEEDED, bool *was_unknown UNNEEDED) +{ fprintf(stderr, "handle_node_announcement called!\n"); abort(); } +/* Generated stub for json_add_member */ +void json_add_member(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, + bool quote UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "json_add_member called!\n"); abort(); } +/* Generated stub for json_member_direct */ +char *json_member_direct(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, size_t extra UNNEEDED) +{ fprintf(stderr, "json_member_direct called!\n"); abort(); } +/* Generated stub for json_object_end */ +void json_object_end(struct json_stream *js UNNEEDED) +{ fprintf(stderr, "json_object_end called!\n"); abort(); } +/* Generated stub for json_object_start */ +void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) +{ fprintf(stderr, "json_object_start 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_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) +{ fprintf(stderr, "notleak_ called!\n"); abort(); } +/* Generated stub for queue_peer_msg */ +void queue_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) +{ fprintf(stderr, "queue_peer_msg called!\n"); abort(); } +/* Generated stub for status_failed */ +void status_failed(enum status_failreason code UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "status_failed 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_hsmd_cupdate_sig_req */ +u8 *towire_hsmd_cupdate_sig_req(const tal_t *ctx UNNEEDED, const u8 *cu UNNEEDED) +{ fprintf(stderr, "towire_hsmd_cupdate_sig_req called!\n"); abort(); } +/* Generated stub for towire_hsmd_node_announcement_sig_req */ +u8 *towire_hsmd_node_announcement_sig_req(const tal_t *ctx UNNEEDED, const u8 *announcement UNNEEDED) +{ fprintf(stderr, "towire_hsmd_node_announcement_sig_req called!\n"); abort(); } +/* Generated stub for towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } +/* Generated stub for wire_sync_read */ +u8 *wire_sync_read(const tal_t *ctx UNNEEDED, int fd UNNEEDED) +{ fprintf(stderr, "wire_sync_read called!\n"); abort(); } +/* Generated stub for wire_sync_write */ +bool wire_sync_write(int fd UNNEEDED, const void *msg TAKES UNNEEDED) +{ fprintf(stderr, "wire_sync_write called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +/* Overwriting this to return test data */ +const u8 *gossip_store_get(const tal_t *ctx, + struct gossip_store *gs, + u64 offset) +{ + /* No TLV, different */ + if (offset == 0) + return tal_hexdata(ctx, "01010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078000000802aaa260b145790266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000", strlen("01010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078000000802aaa260b145790266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000")); + + /* No TLV, same */ + if (offset == 1) + return tal_hexdata(ctx, "01017d49b51b7d3772636c09901df78c81faa1a7f045e329366ad779ecbaf0cc07f764d8ffd3596ef6802fd0e4c5c180c74fb3dfb14aae493ed48d35a3df75c20eca00078000000802aaa260b14569022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d3236352d67373833393533312d6d6f646465640000", strlen("01017d49b51b7d3772636c09901df78c81faa1a7f045e329366ad779ecbaf0cc07f764d8ffd3596ef6802fd0e4c5c180c74fb3dfb14aae493ed48d35a3df75c20eca00078000000802aaa260b14569022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d3236352d67373833393533312d6d6f646465640000")); + + /* Same, with TLV */ + if (offset == 2) + return tal_hexdata(ctx, "0101069628d227649cc2823d94647ad08ea34ad24e7eea95b7a0249bc83e73efefa6072cab1841f0ef3e6d7f4c4140b7b1b13049eb0d85941d7d7bd30c921bfd550300078000000802aaa260b1496f0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000010c0014000003e80096001607d0", strlen("0101069628d227649cc2823d94647ad08ea34ad24e7eea95b7a0249bc83e73efefa6072cab1841f0ef3e6d7f4c4140b7b1b13049eb0d85941d7d7bd30c921bfd550300078000000802aaa260b1496f0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000010c0014000003e80096001607d0")); + + return NULL; +} + +int main(int argc, char *argv[]) +{ + struct node node; + bool only_missing_tlv; + u8 *ann; + + common_setup(argv[0]); + + /* No TLV checks */ + node.bcast.index = 1; + ann = tal_hexdata(tmpctx, "01017d49b51b7d3772636c09901df78c81faa1a7f045e329366ad779ecbaf0cc07f764d8ffd3596ef6802fd0e4c5c180c74fb3dfb14aae493ed48d35a3df75c20eca00078000000802aaa260b14569022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d3236352d67373833393533312d6d6f646465640000", strlen("01017d49b51b7d3772636c09901df78c81faa1a7f045e329366ad779ecbaf0cc07f764d8ffd3596ef6802fd0e4c5c180c74fb3dfb14aae493ed48d35a3df75c20eca00078000000802aaa260b14569022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d3236352d67373833393533312d6d6f646465640000")); + assert(!nannounce_different(NULL, &node, ann, &only_missing_tlv)); + assert(!only_missing_tlv); + + node.bcast.index = 0; + assert(nannounce_different(NULL, &node, ann, &only_missing_tlv)); + assert(!only_missing_tlv); + + /* TLV checks */ + ann = tal_hexdata(tmpctx, "0101069628d227649cc2823d94647ad08ea34ad24e7eea95b7a0249bc83e73efefa6072cab1841f0ef3e6d7f4c4140b7b1b13049eb0d85941d7d7bd30c921bfd550300078000000802aaa260b1496f0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000010c0014000003e80096001607d0", strlen("0101069628d227649cc2823d94647ad08ea34ad24e7eea95b7a0249bc83e73efefa6072cab1841f0ef3e6d7f4c4140b7b1b13049eb0d85941d7d7bd30c921bfd550300078000000802aaa260b1496f0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000010c0014000003e80096001607d0")); + + node.bcast.index = 2; + assert(!nannounce_different(NULL, &node, ann, &only_missing_tlv)); + assert(!only_missing_tlv); + + /* Tweak the last, check that it fails */ + node.bcast.index = 2; + ann[tal_count(ann) - 1]++; + assert(nannounce_different(NULL, &node, ann, &only_missing_tlv)); + assert(only_missing_tlv); + + common_shutdown(); + return 0; +} diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index 38cde87d4..bc811554a 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -70,7 +70,8 @@ void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intma /* Generated stub for nannounce_different */ bool nannounce_different(struct gossip_store *gs UNNEEDED, const struct node *node UNNEEDED, - const u8 *nannounce UNNEEDED) + const u8 *nannounce UNNEEDED, + bool *only_missing_tlv UNNEEDED) { fprintf(stderr, "nannounce_different called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED)