/* * Relay Pool Test Program * * Tests the nostr_relay_pool functionality with persistent connections * and subscriptions. Prints events as they arrive and shows connection status. * * Usage: ./pool_test * Press Ctrl+C to exit */ #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include "../nostr_core/nostr_core.h" #include "../cjson/cJSON.h" // Global variables for signal handling volatile sig_atomic_t running = 1; time_t last_status_time = 0; // Signal handler for clean shutdown void signal_handler(int signum) { (void)signum; // Suppress unused parameter warning printf("\n๐Ÿ›‘ Received signal, shutting down...\n"); running = 0; } // Event callback - called when an event is received void on_event(cJSON* event, const char* relay_url, void* user_data) { (void)user_data; // Suppress unused parameter warning // Extract basic event information cJSON* id = cJSON_GetObjectItem(event, "id"); cJSON* pubkey = cJSON_GetObjectItem(event, "pubkey"); cJSON* created_at = cJSON_GetObjectItem(event, "created_at"); cJSON* kind = cJSON_GetObjectItem(event, "kind"); cJSON* content = cJSON_GetObjectItem(event, "content"); printf("\n๐Ÿ“จ EVENT from %s\n", relay_url); printf("โ”œโ”€โ”€ ID: %.12s...\n", id && cJSON_IsString(id) ? cJSON_GetStringValue(id) : "unknown"); printf("โ”œโ”€โ”€ Pubkey: %.12s...\n", pubkey && cJSON_IsString(pubkey) ? cJSON_GetStringValue(pubkey) : "unknown"); printf("โ”œโ”€โ”€ Kind: %d\n", kind && cJSON_IsNumber(kind) ? (int)cJSON_GetNumberValue(kind) : -1); printf("โ”œโ”€โ”€ Created: %lld\n", created_at && cJSON_IsNumber(created_at) ? (long long)cJSON_GetNumberValue(created_at) : 0); // Truncate content if too long if (content && cJSON_IsString(content)) { const char* content_str = cJSON_GetStringValue(content); size_t content_len = strlen(content_str); if (content_len > 100) { printf("โ””โ”€โ”€ Content: %.97s...\n", content_str); } else { printf("โ””โ”€โ”€ Content: %s\n", content_str); } } else { printf("โ””โ”€โ”€ Content: (empty)\n"); } fflush(stdout); } // EOSE callback - called when End of Stored Events is received void on_eose(cJSON** events, int event_count, void* user_data) { (void)user_data; // Suppress unused parameter warning printf("๐Ÿ“‹ EOSE received - %d events collected\n", event_count); // Log collected events if any for (int i = 0; i < event_count; i++) { cJSON* id = cJSON_GetObjectItem(events[i], "id"); if (id && cJSON_IsString(id)) { printf(" Event %d: %.12s...\n", i + 1, cJSON_GetStringValue(id)); } } fflush(stdout); } // Print connection status for all relays void print_relay_status(nostr_relay_pool_t* pool) { char** relay_urls = NULL; nostr_pool_relay_status_t* statuses = NULL; int relay_count = nostr_relay_pool_list_relays(pool, &relay_urls, &statuses); if (relay_count <= 0) { printf("โŒ No relays in pool\n"); return; } printf("\n๐Ÿ“Š RELAY STATUS (%d relays):\n", relay_count); for (int i = 0; i < relay_count; i++) { const char* status_str; switch (statuses[i]) { case NOSTR_POOL_RELAY_CONNECTED: status_str = "๐ŸŸข CONNECTED"; break; case NOSTR_POOL_RELAY_CONNECTING: status_str = "๐ŸŸก CONNECTING"; break; case NOSTR_POOL_RELAY_DISCONNECTED: status_str = "โšช DISCONNECTED"; break; case NOSTR_POOL_RELAY_ERROR: status_str = "๐Ÿ”ด ERROR"; break; default: status_str = "โ“ UNKNOWN"; break; } printf("โ”œโ”€โ”€ %s: %s\n", relay_urls[i], status_str); // Show additional stats if available const nostr_relay_stats_t* stats = nostr_relay_pool_get_relay_stats(pool, relay_urls[i]); if (stats) { printf("โ”‚ โ”œโ”€โ”€ Events received: %d\n", stats->events_received); printf("โ”‚ โ”œโ”€โ”€ Connection attempts: %d\n", stats->connection_attempts); printf("โ”‚ โ””โ”€โ”€ Connection failures: %d\n", stats->connection_failures); } free(relay_urls[i]); } printf("\n"); free(relay_urls); free(statuses); fflush(stdout); } int main() { printf("๐Ÿ”— NOSTR Relay Pool Test\n"); printf("========================\n"); printf("Testing persistent relay connections with subscriptions.\n"); printf("Press Ctrl+C to exit.\n\n"); // Initialize NOSTR library if (nostr_init() != NOSTR_SUCCESS) { fprintf(stderr, "โŒ Failed to initialize NOSTR library\n"); return 1; } // Setup signal handler for clean shutdown signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); // Create relay pool with default configuration nostr_pool_reconnect_config_t* config = nostr_pool_reconnect_config_default(); nostr_relay_pool_t* pool = nostr_relay_pool_create(config); if (!pool) { fprintf(stderr, "โŒ Failed to create relay pool\n"); nostr_cleanup(); return 1; } // Add relays to the pool const char* relay_urls[] = { "wss://nostr.mom", "wss://relay.laantungir.net", "wss://nos.lol" }; int relay_count = 3; printf("๐Ÿ“ก Adding %d relays to pool:\n", relay_count); for (int i = 0; i < relay_count; i++) { printf("โ”œโ”€โ”€ %s\n", relay_urls[i]); if (nostr_relay_pool_add_relay(pool, relay_urls[i]) != NOSTR_SUCCESS) { printf("โ”‚ โŒ Failed to add relay\n"); } else { printf("โ”‚ โœ… Added successfully\n"); } } printf("\n"); // Create filter for subscription (kind 1 events - text notes) cJSON* filter = cJSON_CreateObject(); cJSON* kinds = cJSON_CreateArray(); cJSON_AddItemToArray(kinds, cJSON_CreateNumber(1)); cJSON_AddItemToObject(filter, "kinds", kinds); cJSON_AddItemToObject(filter, "limit", cJSON_CreateNumber(10)); // Limit to 10 events per relay printf("๐Ÿ” Creating subscription with filter:\n"); char* filter_json = cJSON_Print(filter); printf("%s\n\n", filter_json); free(filter_json); // Create subscription with new parameters nostr_pool_subscription_t* subscription = nostr_relay_pool_subscribe( pool, relay_urls, relay_count, filter, on_event, // Event callback on_eose, // EOSE callback NULL, // User data (not used) 0, // close_on_eose (false - keep subscription open) 1, // enable_deduplication NOSTR_POOL_EOSE_FULL_SET, // result_mode 30, // relay_timeout_seconds 60 // eose_timeout_seconds ); if (!subscription) { fprintf(stderr, "โŒ Failed to create subscription\n"); cJSON_Delete(filter); nostr_relay_pool_destroy(pool); nostr_cleanup(); return 1; } printf("โœ… Subscription created successfully\n"); printf("๐ŸŽฏ Listening for events... (Ctrl+C to exit)\n\n"); // Record start time for status updates last_status_time = time(NULL); // Main event loop while (running) { // Poll for events (100ms timeout) int events_processed = nostr_relay_pool_poll(pool, 100); // Check if we should print status (every 30 seconds) time_t current_time = time(NULL); if (current_time - last_status_time >= 30) { print_relay_status(pool); last_status_time = current_time; } // Small delay to prevent busy waiting if (events_processed == 0) { struct timespec ts = {0, 10000000}; // 10ms nanosleep(&ts, NULL); } } printf("\n๐Ÿงน Cleaning up...\n"); // Close subscription if (subscription) { nostr_pool_subscription_close(subscription); printf("โœ… Subscription closed\n"); } // Destroy pool nostr_relay_pool_destroy(pool); printf("โœ… Relay pool destroyed\n"); // Cleanup JSON cJSON_Delete(filter); // Cleanup library nostr_cleanup(); printf("๐Ÿ‘‹ Test completed successfully\n"); return 0; }