diff --git a/common/gossip_store.h b/common/gossip_store.h index f1a8525df..200c1be6f 100644 --- a/common/gossip_store.h +++ b/common/gossip_store.h @@ -39,11 +39,6 @@ struct gossip_rcvd_filter; */ #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 d8e7e3a6b..0a93e6614 100644 --- a/common/gossmap.c +++ b/common/gossmap.c @@ -651,9 +651,6 @@ static bool map_catchup(struct gossmap *map, bool *changed) 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; diff --git a/contrib/pyln-client/pyln/client/gossmap.py b/contrib/pyln-client/pyln/client/gossmap.py index 88641cbba..275675960 100755 --- a/contrib/pyln-client/pyln/client/gossmap.py +++ b/contrib/pyln-client/pyln/client/gossmap.py @@ -17,7 +17,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 @@ -92,7 +91,6 @@ class GossipStoreMsgHeader(object): 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 +622,6 @@ class Gossmap(object): 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/devtools/dump-gossipstore.c b/devtools/dump-gossipstore.c index 09c6f3a1e..dcb5576b4 100644 --- a/devtools/dump-gossipstore.c +++ b/devtools/dump-gossipstore.c @@ -68,13 +68,12 @@ 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, ratelimit, 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 +84,10 @@ 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%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/gossipd/gossip_store.c b/gossipd/gossip_store.c index 89ad5ba34..c344df08f 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -15,9 +15,12 @@ #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 { /* Back pointer. */ @@ -59,7 +62,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) + bool spam, bool dying, u64 *len) { struct gossip_hdr hdr; u32 msglen; @@ -73,8 +76,6 @@ static bool append_msg(int fd, const u8 *msg, u32 timestamp, 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)); @@ -97,10 +98,11 @@ 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 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 @@ -154,6 +156,7 @@ static void give_lightningd_canned_private_update(struct daemon *daemon, static bool upgrade_field(u8 oldversion, struct daemon *daemon, + u16 hdr_flags, u8 **msg) { int type = fromwire_peektype(*msg); @@ -175,6 +178,12 @@ static bool upgrade_field(u8 oldversion, *msg = tal_free(*msg); } } + if (oldversion <= 13) { + /* Discard any zombies */ + if (hdr_flags & GOSSIP_STORE_ZOMBIE_BIT_V13) { + *msg = tal_free(*msg); + } + } return true; } @@ -250,7 +259,8 @@ static u32 gossip_store_compact_offline(struct daemon *daemon) } if (oldversion != version) { - if (!upgrade_field(oldversion, daemon, &msg)) { + if (!upgrade_field(oldversion, daemon, + be16_to_cpu(hdr.flags), &msg)) { tal_free(msg); goto close_and_delete; } @@ -299,7 +309,7 @@ static u32 gossip_store_compact_offline(struct daemon *daemon) 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); + 0, false, false, &oldlen); close(old_fd); status_debug("gossip_store_compact_offline: %zu deleted, %zu copied", deleted, count); @@ -357,7 +367,7 @@ struct gossip_store *gossip_store_new(struct daemon *daemon) } u64 gossip_store_add(struct gossip_store *gs, const u8 *gossip_msg, - u32 timestamp, bool zombie, + u32 timestamp, bool spam, bool dying, const u8 *addendum) { u64 off = gs->len; @@ -365,12 +375,12 @@ u64 gossip_store_add(struct gossip_store *gs, const u8 *gossip_msg, /* Should never get here during loading! */ assert(gs->writable); - if (!append_msg(gs->fd, gossip_msg, timestamp, zombie, spam, dying, &gs->len)) { + if (!append_msg(gs->fd, gossip_msg, timestamp, 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)) { + if (addendum && !append_msg(gs->fd, addendum, 0, false, false, &gs->len)) { status_broken("Failed writing addendum to gossip store: %s", strerror(errno)); return 0; @@ -504,53 +514,7 @@ 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)); -} - -/* 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); + 0, false, false, NULL); } u32 gossip_store_get_timestamp(struct gossip_store *gs, u64 offset) @@ -737,7 +701,7 @@ u32 gossip_store_load(struct gossip_store *gs) if (!routing_add_channel_update(gs->daemon->rstate, take(msg), gs->len, NULL, false, - spam, false)) { + spam)) { bad = "Bad channel_update"; goto badmsg; } diff --git a/gossipd/gossip_store.h b/gossipd/gossip_store.h index f0d95cf0f..b6e83dce5 100644 --- a/gossipd/gossip_store.h +++ b/gossipd/gossip_store.h @@ -32,14 +32,13 @@ u32 gossip_store_load(struct gossip_store *gs); * @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, + u32 timestamp, bool spam, bool dying, const u8 *addendum); @@ -80,17 +79,6 @@ void gossip_store_flag(struct gossip_store *gs, 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); - /** * Mark this channel_announcement/channel_update as dying. * diff --git a/gossipd/routing.c b/gossipd/routing.c index 0779fa5c6..7d729e173 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -312,25 +312,6 @@ static struct node *new_node(struct routing_state *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) @@ -339,8 +320,6 @@ bool node_has_broadcastable_channels(const struct node *node) 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; @@ -354,10 +333,6 @@ static bool node_announce_predates_channels(const struct node *node) 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; } @@ -381,7 +356,6 @@ static void force_node_announce_rexmit(struct routing_state *rstate, node->bcast.timestamp, false, false, - false, NULL); if (node->rgraph.index == initial_bcast_index){ node->rgraph.index = node->bcast.index; @@ -393,7 +367,6 @@ static void force_node_announce_rexmit(struct routing_state *rstate, node->rgraph.index = gossip_store_add(rstate->daemon->gs, announce, node->rgraph.timestamp, - false, true, false, NULL); @@ -490,7 +463,6 @@ static void init_half_chan(struct routing_state *rstate, broadcastable_init(&c->bcast); broadcastable_init(&c->rgraph); c->tokens = TOKEN_MAX; - c->zombie = false; } static void bad_gossip_order(const u8 *msg, @@ -677,7 +649,6 @@ static void add_channel_announce_to_broadcast(struct routing_state *rstate, chan->bcast.timestamp, false, false, - false, addendum); } @@ -702,9 +673,8 @@ static void delete_chan_messages_from_store(struct routing_state *rstate, 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->daemon->gs, &chan->scid); + /* Put in tombstone marker. */ + gossip_store_mark_channel_deleted(rstate->daemon->gs, &chan->scid); /* Now delete old entries. */ delete_chan_messages_from_store(rstate, chan); @@ -1115,8 +1085,7 @@ bool routing_add_channel_update(struct routing_state *rstate, /* NULL if it's us */ const struct node_id *source_peer, bool ignore_timestamp, - bool force_spam_flag, - bool force_zombie_flag) + bool force_spam_flag) { secp256k1_ecdsa_signature signature; struct short_channel_id short_channel_id; @@ -1133,7 +1102,6 @@ bool routing_add_channel_update(struct routing_state *rstate, u8 direction; struct amount_sat sat; bool spam; - bool zombie; bool dying; /* Make sure we own msg, even if we don't save it. */ @@ -1155,7 +1123,6 @@ bool routing_add_channel_update(struct routing_state *rstate, 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? */ @@ -1172,8 +1139,6 @@ bool routing_add_channel_update(struct routing_state *rstate, return false; } sat = uc->sat; - /* When loading zombies from the store. */ - zombie = force_zombie_flag; dying = false; } @@ -1197,9 +1162,6 @@ bool routing_add_channel_update(struct routing_state *rstate, 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 */ @@ -1302,68 +1264,6 @@ bool routing_add_channel_update(struct routing_state *rstate, uc->index); } - /* Handle resurrection of zombie channels if the other side of the - * zombie channel has a recent timestamp. */ - if (zombie && timestamp_reasonable(rstate->daemon, - 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->daemon->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->daemon->gs, - chan->bcast.index + offset); - gossip_store_delete(rstate->daemon->gs, &chan->bcast, - WIRE_CHANNEL_ANNOUNCEMENT); - chan->bcast.index = - gossip_store_add(rstate->daemon->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->daemon->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->daemon->gs, - chan->half[!direction].rgraph.index); - gossip_store_delete(rstate->daemon->gs, &chan->half[!direction].rgraph, - WIRE_CHANNEL_UPDATE); - } - gossip_store_delete(rstate->daemon->gs, &chan->half[!direction].bcast, - WIRE_CHANNEL_UPDATE); - chan->half[!direction].bcast.index = - gossip_store_add(rstate->daemon->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->daemon->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) @@ -1372,7 +1272,7 @@ bool routing_add_channel_update(struct routing_state *rstate, } else { hc->rgraph.index = gossip_store_add(rstate->daemon->gs, update, timestamp, - zombie, spam, dying, NULL); + spam, dying, NULL); if (!spam) hc->bcast.index = hc->rgraph.index; @@ -1540,7 +1440,7 @@ u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, } routing_add_channel_update(rstate, take(serialized), 0, source_peer, force, - false, false); + false); return NULL; } @@ -1674,8 +1574,7 @@ bool routing_add_node_announcement(struct routing_state *rstate, if (!pna) { if (was_unknown) *was_unknown = true; - /* Don't complain if it's a zombie node! */ - if (!node || !is_node_zombie(node)) { + if (!node) { bad_gossip_order(msg, source_peer, type_to_string(tmpctx, struct node_id, &node_id)); @@ -1788,7 +1687,7 @@ bool routing_add_node_announcement(struct routing_state *rstate, } else { node->rgraph.index = gossip_store_add(rstate->daemon->gs, msg, timestamp, - false, spam, false, NULL); + spam, false, NULL); if (!spam) node->bcast.index = node->rgraph.index; @@ -1882,10 +1781,6 @@ void route_prune(struct routing_state *rstate) 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): @@ -2061,7 +1956,7 @@ void routing_channel_spent(struct routing_state *rstate, /* Save to gossip_store in case we restart */ msg = towire_gossip_store_chan_dying(tmpctx, &chan->scid, deadline); - index = gossip_store_add(rstate->daemon->gs, msg, 0, false, false, false, NULL); + index = gossip_store_add(rstate->daemon->gs, msg, 0, false, false, NULL); /* Mark it dying, so we don't gossip it */ gossip_store_mark_dying(rstate->daemon->gs, &chan->bcast, diff --git a/gossipd/routing.h b/gossipd/routing.h index c44eef3d3..85329c6f8 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -30,9 +30,6 @@ struct half_chan { /* Token bucket */ u8 tokens; - - /* Disabled channel waiting for a channel_update from both sides. */ - bool zombie; }; struct chan { @@ -319,8 +316,7 @@ bool routing_add_channel_update(struct routing_state *rstate, u32 index, const struct node_id *source_peer TAKES, bool ignore_timestamp, - bool force_spam_flag, - bool force_zombie_flag); + bool force_spam_flag); /** * Add a node_announcement to the network view without checking it * @@ -364,7 +360,7 @@ bool would_ratelimit_cupdate(struct routing_state *rstate, const struct half_chan *hc, u32 timestamp); -/* Does this node have public, non-zombie channels? */ +/* Does this node have public channels? */ bool node_has_broadcastable_channels(const struct node *node); /* Returns an error string if there are unfinalized entries after load */ diff --git a/tests/test_gossip.py b/tests/test_gossip.py index a11fd86ed..26a24fa90 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1616,7 +1616,7 @@ def test_gossip_store_load_no_channel_update(node_factory): # This should actually result in an empty store. with open(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store'), "rb") as f: - assert bytearray(f.read()) == bytearray.fromhex("0d") + assert bytearray(f.read()) == bytearray.fromhex("0e") def test_gossip_store_compact_on_load(node_factory, bitcoind): @@ -2094,74 +2094,6 @@ def test_gossip_not_dying(node_factory, bitcoind): 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)