@@ -62,6 +62,105 @@ void RelayServer::runCron() {
6262
6363
6464
65+ // NIP-62: Delete events for vanished pubkeys
66+
67+ cron.repeat (30 * 1'000'000UL , [&]{
68+ if (!cfg ().relay__nip62__enabled ) return ;
69+
70+ struct VanishEntry {
71+ std::string pubkey;
72+ uint64_t vanishTs;
73+ };
74+
75+ std::vector<VanishEntry> vanishEntries;
76+ std::vector<uint64_t > vanishLevIds;
77+ const uint64_t batchLimit = 10000 ;
78+
79+ {
80+ auto txn = env.txn_ro ();
81+
82+ // Collect all vanish entries
83+ {
84+ auto cursor = lmdb::cursor::open (txn, env.dbi_VanishPubkey );
85+ std::string_view k, v;
86+ if (cursor.get (k, v, MDB_FIRST)) {
87+ do {
88+ if (k.size () == 32 && v.size () == sizeof (uint64_t )) {
89+ vanishEntries.push_back ({std::string (k), lmdb::from_sv<uint64_t >(v)});
90+ }
91+ } while (cursor.get (k, v, MDB_NEXT));
92+ }
93+ }
94+
95+ for (auto &entry : vanishEntries) {
96+ // Scan pubkey index for events authored by this pubkey
97+ auto searchPrefix = std::string (entry.pubkey );
98+ auto startKey = makeKey_StringUint64 (searchPrefix, 0 );
99+
100+ env.generic_foreachFull (txn, env.dbi_Event__pubkey , startKey, lmdb::to_sv<uint64_t >(0 ), [&](auto k, auto v) {
101+ if (!k.starts_with (searchPrefix)) return false ;
102+
103+ auto levId = lmdb::from_sv<uint64_t >(v);
104+ auto ev = env.lookup_Event (txn, levId);
105+ if (!ev) return true ;
106+
107+ PackedEventView packed (ev->buf );
108+
109+ // Skip kind 62 events (preserve bookkeeping)
110+ if (packed.kind () == 62 ) return true ;
111+
112+ if (packed.created_at () <= entry.vanishTs ) {
113+ vanishLevIds.push_back (levId);
114+ if (vanishLevIds.size () >= batchLimit) {
115+ return false ;
116+ }
117+ }
118+
119+ return true ;
120+ });
121+
122+ // Scan tag index for gift wraps (kind 1059) addressed to this pubkey
123+ if (vanishLevIds.size () < batchLimit) {
124+ auto tagPrefix = std::string (" p" ) + entry.pubkey ;
125+ auto tagStartKey = makeKey_StringUint64 (tagPrefix, 0 );
126+
127+ env.generic_foreachFull (txn, env.dbi_Event__tag , tagStartKey, lmdb::to_sv<uint64_t >(0 ), [&](auto k, auto v) {
128+ if (k.size () != tagPrefix.size () + 8 || !k.starts_with (tagPrefix)) return false ;
129+
130+ auto levId = lmdb::from_sv<uint64_t >(v);
131+ auto ev = env.lookup_Event (txn, levId);
132+ if (!ev) return true ;
133+
134+ PackedEventView packed (ev->buf );
135+
136+ // Delete all gift wraps (kind 1059) addressed to this pubkey (spec says ALL, no timestamp qualifier)
137+ if (packed.kind () == 1059 ) {
138+ vanishLevIds.push_back (levId);
139+ if (vanishLevIds.size () >= batchLimit) {
140+ return false ;
141+ }
142+ }
143+
144+ return true ;
145+ });
146+ }
147+
148+ }
149+ }
150+
151+ if (vanishLevIds.size () > 0 ) {
152+ auto txn = env.txn_rw ();
153+ NegentropyFilterCache neFilterCache;
154+
155+ uint64_t numDeleted = deleteEvents (txn, neFilterCache, vanishLevIds);
156+
157+ txn.commit ();
158+
159+ if (numDeleted) LI << " NIP-62 vanish: deleted " << numDeleted << " events" ;
160+ }
161+ });
162+
163+
65164 cron.run ();
66165
67166 while (1 ) std::this_thread::sleep_for (std::chrono::seconds (1'000'000 ));
0 commit comments