#include #include #include #include #include #include #include #include #include #include #include #include #include #include struct queued_message { struct broadcastable *bcast; /* Serialized payload */ const u8 *payload; }; struct broadcast_state *new_broadcast_state(struct routing_state *rstate) { struct broadcast_state *bstate = tal(rstate, struct broadcast_state); uintmap_init(&bstate->broadcasts); bstate->count = 0; bstate->gs = gossip_store_new(rstate); return bstate; } void broadcast_del(struct broadcast_state *bstate, struct broadcastable *bcast) { const struct queued_message *q = uintmap_del(&bstate->broadcasts, bcast->index); if (q != NULL) { assert(q->bcast == bcast); tal_free(q); bstate->count--; broadcast_state_check(bstate, "broadcast_del"); bcast->index = 0; } } static struct queued_message *new_queued_message(struct broadcast_state *bstate, const u8 *payload, struct broadcastable *bcast) { struct queued_message *msg = tal(bstate, struct queued_message); assert(payload); assert(bcast); assert(bcast->index); msg->payload = payload; msg->bcast = bcast; if (!uintmap_add(&bstate->broadcasts, bcast->index, msg)) abort(); bstate->count++; return msg; } void insert_broadcast_nostore(struct broadcast_state *bstate, const u8 *msg, struct broadcastable *bcast) { new_queued_message(bstate, msg, bcast); broadcast_state_check(bstate, "insert_broadcast"); } void insert_broadcast(struct broadcast_state **bstate, const u8 *msg, struct broadcastable *bcast) { /* If we're loading from the store, we already have index */ if (!bcast->index) { u64 idx; bcast->index = idx = gossip_store_add((*bstate)->gs, msg); if (!idx) status_failed(STATUS_FAIL_INTERNAL_ERROR, "Could not add to gossip store: %s", strerror(errno)); /* We assume we can fit in 32 bits for now! */ assert(idx == bcast->index); } insert_broadcast_nostore(*bstate, msg, bcast); /* If it compacts, it replaces *bstate */ gossip_store_maybe_compact((*bstate)->gs, bstate); } const u8 *pop_first_broadcast(struct broadcast_state *bstate, struct broadcastable **bcast) { u64 idx; const u8 *msg; struct queued_message *q = uintmap_first(&bstate->broadcasts, &idx); if (!q) return NULL; *bcast = q->bcast; msg = q->payload; broadcast_del(bstate, *bcast); return msg; } const u8 *next_broadcast(struct broadcast_state *bstate, u32 timestamp_min, u32 timestamp_max, u32 *last_index) { struct queued_message *m; u64 idx = *last_index; while ((m = uintmap_after(&bstate->broadcasts, &idx)) != NULL) { if (m->bcast->timestamp >= timestamp_min && m->bcast->timestamp <= timestamp_max) { *last_index = idx; return m->payload; } } return NULL; } u64 broadcast_final_index(const struct broadcast_state *bstate) { u64 idx; if (!uintmap_last(&bstate->broadcasts, &idx)) return 0; return idx; } #ifdef PEDANTIC static const struct pubkey * pubkey_keyof(const struct pubkey *pk) { return pk; } static size_t pubkey_hash(const struct pubkey *id) { return siphash24(siphash_seed(), id, sizeof(*id)); } HTABLE_DEFINE_TYPE(struct pubkey, pubkey_keyof, pubkey_hash, pubkey_eq, pubkey_set); static void *corrupt(const char *abortstr, const char *problem, const struct short_channel_id *scid, const struct pubkey *node_id) { status_broken("Gossip corrupt %s %s: %s", problem, abortstr ? abortstr : "", scid ? type_to_string(tmpctx, struct short_channel_id, scid) : type_to_string(tmpctx, struct pubkey, node_id)); if (abortstr) abort(); return NULL; } struct broadcast_state *broadcast_state_check(struct broadcast_state *b, const char *abortstr) { secp256k1_ecdsa_signature sig; const u8 *msg; u8 *features, *addresses, color[3], alias[32]; struct bitcoin_blkid chain_hash; struct short_channel_id scid; struct pubkey node_id_1, node_id_2, bitcoin_key; u32 timestamp, fees; u16 flags, expiry; u32 index = 0; u64 htlc_minimum_msat; struct pubkey_set pubkeys; /* We actually only need a set, not a map. */ UINTMAP(u64 *) channels; pubkey_set_init(&pubkeys); uintmap_init(&channels); while ((msg = next_broadcast(b, 0, UINT32_MAX, &index)) != NULL) { if (fromwire_channel_announcement(tmpctx, msg, &sig, &sig, &sig, &sig, &features, &chain_hash, &scid, &node_id_1, &node_id_2, &bitcoin_key, &bitcoin_key)) { if (!uintmap_add(&channels, scid.u64, &index)) return corrupt(abortstr, "announced twice", &scid, NULL); pubkey_set_add(&pubkeys, &node_id_1); pubkey_set_add(&pubkeys, &node_id_2); } else if (fromwire_channel_update(msg, &sig, &chain_hash, &scid, ×tamp, &flags, &expiry, &htlc_minimum_msat, &fees, &fees)) { if (!uintmap_get(&channels, scid.u64)) return corrupt(abortstr, "updated before announce", &scid, NULL); } else if (fromwire_node_announcement(tmpctx, msg, &sig, &features, ×tamp, &node_id_1, color, alias, &addresses)) if (!uintmap_get(&channels, scid.u64)) return corrupt(abortstr, "node announced before channel", NULL, &node_id_1); } pubkey_set_clear(&pubkeys); uintmap_clear(&channels); return b; } #else struct broadcast_state *broadcast_state_check(struct broadcast_state *b, const char *abortstr UNUSED) { return b; } #endif