319 lines
10 KiB
C
319 lines
10 KiB
C
/*
|
||
* 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 <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
#include <signal.h>
|
||
#include <time.h>
|
||
#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("<EFBFBD> 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;
|
||
}
|