/* * NOSTR Relay Pool Test Program (READ-ONLY) * * Tests the relay pool event processing functionality by: * - Creating a pool with hardcoded relays * - Subscribing to kind 1 events (text notes) from other users * - Using the new event processing functions * - Displaying raw data output without interpretation * * IMPORTANT: This test is READ-ONLY and never publishes events. * It only sends REQ (subscription) messages and receives EVENT responses. * Any test events seen in output are from other users or previous test runs. */ #include #include #include #include #include #include #include "../nostr_core/nostr_core.h" #include "../cjson/cJSON.h" // Global variables for clean shutdown static volatile int keep_running = 1; static nostr_relay_pool_t* g_pool = NULL; static nostr_pool_subscription_t* g_subscription = NULL; // Statistics tracking static int events_received = 0; static int events_per_relay[3] = {0, 0, 0}; // Track events per relay static const char* relay_urls[] = { "wss://relay.laantungir.net", "ws://127.0.0.1:7777", "wss://nostr.mom" }; static const int relay_count = 3; // Signal handler for clean shutdown void signal_handler(int sig) { (void)sig; // Unused parameter printf("\n๐Ÿ›‘ Received shutdown signal, cleaning up...\n"); keep_running = 0; } // Event callback - called when events are received void on_event_received(cJSON* event, const char* relay_url, void* user_data) { (void)user_data; // Unused parameter events_received++; // Track events per relay for (int i = 0; i < relay_count; i++) { if (strcmp(relay_url, relay_urls[i]) == 0) { events_per_relay[i]++; break; } } // Print raw event data char* event_json = cJSON_Print(event); if (event_json) { printf("\n๐Ÿ“จ EVENT from %s:\n", relay_url); printf("Raw JSON: %s\n", event_json); printf("---\n"); free(event_json); } // Also extract and display key fields for readability cJSON* id = cJSON_GetObjectItem(event, "id"); cJSON* pubkey = cJSON_GetObjectItem(event, "pubkey"); cJSON* created_at = cJSON_GetObjectItem(event, "created_at"); cJSON* content = cJSON_GetObjectItem(event, "content"); printf("๐Ÿ“„ Parsed fields:\n"); if (id && cJSON_IsString(id)) { printf(" ID: %s\n", cJSON_GetStringValue(id)); } if (pubkey && cJSON_IsString(pubkey)) { printf(" Author: %s\n", cJSON_GetStringValue(pubkey)); } if (created_at && cJSON_IsNumber(created_at)) { time_t timestamp = (time_t)cJSON_GetNumberValue(created_at); printf(" Created: %s", ctime(×tamp)); } if (content && cJSON_IsString(content)) { const char* text = cJSON_GetStringValue(content); printf(" Content: %.100s%s\n", text, strlen(text) > 100 ? "..." : ""); } printf("===============================\n"); } // EOSE callback - called when all relays have sent "End of Stored Events" void on_eose_received(void* user_data) { (void)user_data; // Unused parameter printf("โœ… EOSE: All relays have finished sending stored events\n"); } // Display relay status void display_relay_status() { char** urls; nostr_pool_relay_status_t* statuses; int count = nostr_relay_pool_list_relays(g_pool, &urls, &statuses); if (count > 0) { printf("\n๐Ÿ”— RELAY STATUS:\n"); for (int i = 0; i < count; i++) { const char* status_icon; const char* status_text; switch (statuses[i]) { case NOSTR_POOL_RELAY_CONNECTED: status_icon = "๐ŸŸข"; status_text = "Connected"; break; case NOSTR_POOL_RELAY_CONNECTING: status_icon = "๐ŸŸก"; status_text = "Connecting..."; break; case NOSTR_POOL_RELAY_DISCONNECTED: status_icon = "๐Ÿ”ด"; status_text = "Disconnected"; break; case NOSTR_POOL_RELAY_ERROR: status_icon = "โŒ"; status_text = "Error"; break; default: status_icon = "โ“"; status_text = "Unknown"; break; } // Get publish and query latency statistics double query_latency = nostr_relay_pool_get_relay_query_latency(g_pool, urls[i]); const nostr_relay_stats_t* stats = nostr_relay_pool_get_relay_stats(g_pool, urls[i]); // Get events count from relay statistics (more accurate) int relay_events = 0; if (stats) { relay_events = stats->events_received; } else { // Fallback to local counter for (int j = 0; j < relay_count; j++) { if (strcmp(urls[i], relay_urls[j]) == 0) { relay_events = events_per_relay[j]; break; } } } // Display status with latency information if (query_latency >= 0.0) { printf(" %s %-25s %s (query: %.0fms, events: %d)\n", status_icon, urls[i], status_text, query_latency, relay_events); } else { printf(" %s %-25s %s (query: ---, events: %d)\n", status_icon, urls[i], status_text, relay_events); } // Show additional latency statistics if available if (stats) { if (stats->publish_samples > 0) { printf(" ๐Ÿ“Š Publish latency: avg=%.0fms (%d samples)\n", stats->publish_latency_avg, stats->publish_samples); } if (stats->query_samples > 0) { printf(" ๐Ÿ“Š Query latency: avg=%.0fms (%d samples)\n", stats->query_latency_avg, stats->query_samples); } if (stats->events_published > 0) { printf(" ๐Ÿ“ค Published: %d events (%d OK, %d failed)\n", stats->events_published, stats->events_published_ok, stats->events_published_failed); } } free(urls[i]); } free(urls); free(statuses); printf("๐Ÿ“Š Total events received: %d\n", events_received); } } int main() { printf("๐Ÿš€ NOSTR Relay Pool Test Program\n"); printf("=================================\n"); printf("Testing relays:\n"); for (int i = 0; i < relay_count; i++) { printf(" - %s\n", relay_urls[i]); } printf("\n"); // Set up signal handler for clean shutdown signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); // Initialize NOSTR core library printf("๐Ÿ”ง Initializing NOSTR core library...\n"); if (nostr_init() != NOSTR_SUCCESS) { fprintf(stderr, "โŒ Failed to initialize NOSTR core library\n"); return 1; } // Create relay pool printf("๐ŸŠ Creating relay pool...\n"); g_pool = nostr_relay_pool_create(); if (!g_pool) { fprintf(stderr, "โŒ Failed to create relay pool\n"); nostr_cleanup(); return 1; } // Add relays to pool printf("โž• Adding relays to pool...\n"); for (int i = 0; i < relay_count; i++) { printf(" Adding: %s\n", relay_urls[i]); int result = nostr_relay_pool_add_relay(g_pool, relay_urls[i]); if (result != NOSTR_SUCCESS) { printf(" โš ๏ธ Warning: Failed to add relay %s (error: %s)\n", relay_urls[i], nostr_strerror(result)); } } // Create filter for kind 1 events (text notes) printf("๐Ÿ” Creating subscription filter for kind 1 events...\n"); cJSON* filter = cJSON_CreateObject(); if (!filter) { fprintf(stderr, "โŒ Failed to create filter\n"); nostr_relay_pool_destroy(g_pool); nostr_cleanup(); return 1; } // Add kinds array with kind 1 (text notes) cJSON* kinds = cJSON_CreateArray(); cJSON_AddItemToArray(kinds, cJSON_CreateNumber(1)); cJSON_AddItemToObject(filter, "kinds", kinds); // Limit to recent events to avoid flooding cJSON_AddNumberToObject(filter, "limit", 1); // Subscribe to events from all relays printf("๐Ÿ“ก Subscribing to events from all relays...\n"); g_subscription = nostr_relay_pool_subscribe( g_pool, relay_urls, relay_count, filter, on_event_received, on_eose_received, NULL ); if (!g_subscription) { fprintf(stderr, "โŒ Failed to create subscription\n"); cJSON_Delete(filter); nostr_relay_pool_destroy(g_pool); nostr_cleanup(); return 1; } printf("โœ… Subscription created successfully!\n"); printf("โฑ๏ธ Starting event processing...\n"); printf(" (Press Ctrl+C to stop)\n\n"); // Display initial status display_relay_status(); printf("๏ฟฝ Starting continuous monitoring...\n\n"); // Run event processing loop time_t last_status_update = time(NULL); while (keep_running) { // Process events for 1 second int events_processed = nostr_relay_pool_run(g_pool, 1000); // Display status every 5 seconds if (time(NULL) - last_status_update >= 5) { display_relay_status(); last_status_update = time(NULL); } // Small status indicator if (events_processed > 0) { printf("."); fflush(stdout); } } printf("\n\n๐Ÿ Test completed!\n"); // Final status display display_relay_status(); // Cleanup printf("๐Ÿงน Cleaning up...\n"); if (g_subscription) { nostr_pool_subscription_close(g_subscription); } if (g_pool) { nostr_relay_pool_destroy(g_pool); } cJSON_Delete(filter); nostr_cleanup(); printf("โœ… Test program finished successfully!\n"); printf("๐Ÿ“ˆ Final stats:\n"); printf(" Total events: %d\n", events_received); for (int i = 0; i < relay_count; i++) { printf(" %s: %d events\n", relay_urls[i], events_per_relay[i]); } return 0; }