254 lines
8.3 KiB
C
254 lines
8.3 KiB
C
/*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#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;
|
|
} |