From 81c5339063d2e98b833be6e7e1f352b88a417e44 Mon Sep 17 00:00:00 2001 From: Laan Tungir Date: Fri, 31 Jul 2026 13:58:24 -0400 Subject: [PATCH] v2.1.28 - Add kind-3 fallback for relay discovery (NIP-65 pre-standard relay lists in contact list r tags) --- caching/src/relay_discovery.c | 96 +++++++++++++++++++++++++++++++++-- src/main.h | 4 +- 2 files changed, 95 insertions(+), 5 deletions(-) diff --git a/caching/src/relay_discovery.c b/caching/src/relay_discovery.c index 16a671c..7a610f6 100644 --- a/caching/src/relay_discovery.c +++ b/caching/src/relay_discovery.c @@ -239,8 +239,8 @@ static void compute_covering_set(cr_relay_map_t *map, cr_config_t *cfg, * Returns a cJSON array of events (caller must cJSON_Delete each + free). * Sets *out_count to the number of events returned. */ static cJSON **query_kind10002_batch(nostr_relay_pool_t *pool, - const char **pubkeys, int pubkey_count, - int *out_count) { + const char **pubkeys, int pubkey_count, + int *out_count) { cJSON *filter = cJSON_CreateObject(); cJSON *authors = cJSON_CreateArray(); for (int i = 0; i < pubkey_count; i++) @@ -276,6 +276,44 @@ static cJSON **query_kind10002_batch(nostr_relay_pool_t *pool, return events; } +/* ---- batched kind-3 fallback query ---- */ + +/* Query kind 3 (contact list) for a batch of pubkeys from a pool. + * Kind-3 events historically contain "r" tags with relay URLs (pre-NIP-65). + * We use this as a fallback for pubkeys that don't have kind-10002. + * Returns a cJSON array of events (caller must cJSON_Delete each + free). + * Sets *out_count to the number of events returned. */ +static cJSON **query_kind3_batch(nostr_relay_pool_t *pool, + const char **pubkeys, int pubkey_count, + int *out_count) { + cJSON *filter = cJSON_CreateObject(); + cJSON *authors = cJSON_CreateArray(); + for (int i = 0; i < pubkey_count; i++) + cJSON_AddItemToArray(authors, cJSON_CreateString(pubkeys[i])); + cJSON_AddItemToObject(filter, "authors", authors); + cJSON *kinds = cJSON_CreateArray(); + cJSON_AddItemToArray(kinds, cJSON_CreateNumber(3)); + cJSON_AddItemToObject(filter, "kinds", kinds); + /* limit=1 per author: we only need the most recent kind-3. + * The relay pool query_sync returns the most recent events. */ + cJSON_AddItemToObject(filter, "limit", cJSON_CreateNumber(pubkey_count)); + + const char **urls = NULL; + int n = get_pool_urls(pool, &urls); + + DEBUG_TRACE("query_kind3_batch: querying %d relays for %d pubkeys", n, pubkey_count); + + int ev_count = 0; + cJSON **events = nostr_relay_pool_query_sync(pool, urls, n, filter, &ev_count, 30000); + free(urls); + cJSON_Delete(filter); + + DEBUG_TRACE("query_kind3_batch: returned %d events", ev_count); + + *out_count = ev_count; + return events; +} + /* Process a batch of kind-10002 events: parse r-tags, populate outbox entries, * publish to local relay. Returns count of events processed. */ static int process_10002_batch(cJSON **events, int ev_count, @@ -438,9 +476,61 @@ int cr_relay_discovery_run(cr_relay_map_t *map, cr_config_t *cfg, free(remaining_pk); } + /* Phase 3: Kind-3 fallback for pubkeys still without relay lists. + * Kind-3 (contact list) events historically contain "r" tags with relay + * URLs (pre-NIP-65). This catches older users who never published kind-10002. */ + int remaining_k3 = 0; + for (int i = 0; i < followed->count; i++) if (!found[i]) remaining_k3++; + + if (remaining_k3 > 0) { + DEBUG_INFO("relay_discovery: querying bootstrap relays for kind-3 fallback (%d pubkeys)...", + remaining_k3); + + const char **remaining_pk = malloc(remaining_k3 * sizeof(char *)); + int ridx = 0; + for (int i = 0; i < followed->count; i++) { + if (!found[i]) remaining_pk[ridx++] = followed->items[i]; + } + + int found_k3 = 0; + for (int start = 0; start < remaining_k3 && !g_shutdown; start += CR_10002_BATCH_SIZE) { + int batch = remaining_k3 - start; + if (batch > CR_10002_BATCH_SIZE) batch = CR_10002_BATCH_SIZE; + + int ev_count = 0; + cJSON **events = query_kind3_batch(upstream, + &remaining_pk[start], + batch, &ev_count); + if (events && ev_count > 0) { + for (int k = 0; k < ev_count; k++) { + cJSON *pk = cJSON_GetObjectItem(events[k], "pubkey"); + if (pk && cJSON_IsString(pk)) { + const char *hex = cJSON_GetStringValue(pk); + for (int i = 0; i < followed->count; i++) { + if (strcmp(followed->items[i], hex) == 0) { + if (!found[i]) { found[i] = 1; found_k3++; } + break; + } + } + } + } + /* process_10002_batch works for kind-3 too — same r-tag format */ + process_10002_batch(events, ev_count, map, sink, "bootstrap"); + cr_sink_pump(sink, 500); + } else { + free(events); + } + } + free(remaining_pk); + DEBUG_INFO("relay_discovery: kind-3 fallback found %d additional pubkeys with relays", found_k3); + } + found_none = followed->count - found_local - found_bootstrap; + /* Recalculate found_none after kind-3 fallback */ + int still_none = 0; + for (int i = 0; i < followed->count; i++) if (!found[i]) still_none++; DEBUG_INFO("relay_discovery: %d local, %d bootstrap, %d none", - found_local, found_bootstrap, found_none); + found_local, found_bootstrap, still_none); free(found); diff --git a/src/main.h b/src/main.h index f564887..4ed021e 100644 --- a/src/main.h +++ b/src/main.h @@ -13,8 +13,8 @@ // Using CRELAY_ prefix to avoid conflicts with nostr_core_lib VERSION macros #define CRELAY_VERSION_MAJOR 2 #define CRELAY_VERSION_MINOR 1 -#define CRELAY_VERSION_PATCH 27 -#define CRELAY_VERSION "v2.1.27" +#define CRELAY_VERSION_PATCH 28 +#define CRELAY_VERSION "v2.1.28" // Relay metadata (authoritative source for NIP-11 information) #define RELAY_NAME "C-Relay-PG"