Getting the relay pool up to speed

This commit is contained in:
2025-10-02 11:51:41 -04:00
parent 0d910ca181
commit 0f897ab1b3
14 changed files with 6515 additions and 308 deletions

BIN
tests/pool_test Executable file

Binary file not shown.

679
tests/pool_test.c Normal file
View File

@@ -0,0 +1,679 @@
/*
* Interactive Relay Pool Test Program
*
* Interactive command-line interface for testing nostr_relay_pool functionality.
* All output is logged to pool.log while the menu runs in the terminal.
*
* Usage: ./pool_test
*/
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "../nostr_core/nostr_core.h"
#include "../cjson/cJSON.h"
// Global variables
volatile sig_atomic_t running = 1;
nostr_relay_pool_t* pool = NULL;
nostr_pool_subscription_t** subscriptions = NULL;
int subscription_count = 0;
int subscription_capacity = 0;
pthread_t poll_thread;
int log_fd = -1;
// Signal handler for clean shutdown
void signal_handler(int signum) {
(void)signum;
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;
// 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");
time_t now = time(NULL);
char timestamp[26];
ctime_r(&now, timestamp);
timestamp[24] = '\0'; // Remove newline
dprintf(log_fd, "[%s] 📨 EVENT from %s\n", timestamp, relay_url);
dprintf(log_fd, "├── ID: %.12s...\n", id && cJSON_IsString(id) ? cJSON_GetStringValue(id) : "unknown");
dprintf(log_fd, "├── Pubkey: %.12s...\n", pubkey && cJSON_IsString(pubkey) ? cJSON_GetStringValue(pubkey) : "unknown");
dprintf(log_fd, "├── Kind: %d\n", kind && cJSON_IsNumber(kind) ? (int)cJSON_GetNumberValue(kind) : -1);
dprintf(log_fd, "├── 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) {
dprintf(log_fd, "└── Content: %.97s...\n", content_str);
} else {
dprintf(log_fd, "└── Content: %s\n", content_str);
}
} else {
dprintf(log_fd, "└── Content: (empty)\n");
}
dprintf(log_fd, "\n");
}
// EOSE callback - called when End of Stored Events is received
void on_eose(void* user_data) {
(void)user_data;
time_t now = time(NULL);
char timestamp[26];
ctime_r(&now, timestamp);
timestamp[24] = '\0';
dprintf(log_fd, "[%s] 📋 EOSE received - all stored events delivered\n\n", timestamp);
}
// Background polling thread
void* poll_thread_func(void* arg) {
(void)arg;
while (running) {
if (pool) {
nostr_relay_pool_poll(pool, 100);
}
struct timespec ts = {0, 10000000}; // 10ms
nanosleep(&ts, NULL);
}
return NULL;
}
// Print menu
void print_menu() {
printf("\n=== NOSTR Relay Pool Test Menu ===\n");
printf("1. Start Pool (wss://relay.laantungir.net)\n");
printf("2. Stop Pool\n");
printf("3. Add relay to pool\n");
printf("4. Remove relay from pool\n");
printf("5. Add subscription\n");
printf("6. Remove subscription\n");
printf("7. Show pool status\n");
printf("8. Test reconnection (simulate disconnect)\n");
printf("9. Exit\n");
printf("Choice: ");
}
// Get user input with default
char* get_input(const char* prompt, const char* default_value) {
static char buffer[1024];
printf("%s", prompt);
if (default_value) {
printf(" [%s]", default_value);
}
printf(": ");
if (!fgets(buffer, sizeof(buffer), stdin)) {
return NULL;
}
// Remove newline
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n') {
buffer[len-1] = '\0';
}
// Return default if empty
if (strlen(buffer) == 0 && default_value) {
return strdup(default_value);
}
return strdup(buffer);
}
// Parse comma-separated list into cJSON array
cJSON* parse_comma_list(const char* input, int is_number) {
if (!input || strlen(input) == 0) {
return NULL;
}
cJSON* array = cJSON_CreateArray();
if (!array) return NULL;
char* input_copy = strdup(input);
char* token = strtok(input_copy, ",");
while (token) {
// Trim whitespace
while (*token == ' ') token++;
char* end = token + strlen(token) - 1;
while (end > token && *end == ' ') *end-- = '\0';
if (is_number) {
int num = atoi(token);
cJSON_AddItemToArray(array, cJSON_CreateNumber(num));
} else {
cJSON_AddItemToArray(array, cJSON_CreateString(token));
}
token = strtok(NULL, ",");
}
free(input_copy);
return array;
}
// Add subscription interactively
void add_subscription() {
if (!pool) {
printf("❌ Pool not started\n");
return;
}
printf("\n--- Add Subscription ---\n");
printf("Enter filter values (press Enter for no value):\n");
cJSON* filter = cJSON_CreateObject();
// ids
char* ids_input = get_input("ids (comma-separated event ids)", NULL);
if (ids_input && strlen(ids_input) > 0) {
cJSON* ids = parse_comma_list(ids_input, 0);
if (ids) cJSON_AddItemToObject(filter, "ids", ids);
}
free(ids_input);
// authors
char* authors_input = get_input("authors (comma-separated pubkeys)", NULL);
if (authors_input && strlen(authors_input) > 0) {
cJSON* authors = parse_comma_list(authors_input, 0);
if (authors) cJSON_AddItemToObject(filter, "authors", authors);
}
free(authors_input);
// kinds
char* kinds_input = get_input("kinds (comma-separated numbers)", NULL);
if (kinds_input && strlen(kinds_input) > 0) {
cJSON* kinds = parse_comma_list(kinds_input, 1);
if (kinds) cJSON_AddItemToObject(filter, "kinds", kinds);
}
free(kinds_input);
// #e tag
char* e_input = get_input("#e (comma-separated event ids)", NULL);
if (e_input && strlen(e_input) > 0) {
cJSON* e_array = parse_comma_list(e_input, 0);
if (e_array) cJSON_AddItemToObject(filter, "#e", e_array);
}
free(e_input);
// #p tag
char* p_input = get_input("#p (comma-separated pubkeys)", NULL);
if (p_input && strlen(p_input) > 0) {
cJSON* p_array = parse_comma_list(p_input, 0);
if (p_array) cJSON_AddItemToObject(filter, "#p", p_array);
}
free(p_input);
// since
char* since_input = get_input("since (unix timestamp or 'n' for now)", NULL);
if (since_input && strlen(since_input) > 0) {
if (strcmp(since_input, "n") == 0) {
// Use current timestamp
time_t now = time(NULL);
cJSON_AddItemToObject(filter, "since", cJSON_CreateNumber((int)now));
printf("Using current timestamp: %ld\n", now);
} else {
int since = atoi(since_input);
if (since > 0) cJSON_AddItemToObject(filter, "since", cJSON_CreateNumber(since));
}
}
free(since_input);
// until
char* until_input = get_input("until (unix timestamp)", NULL);
if (until_input && strlen(until_input) > 0) {
int until = atoi(until_input);
if (until > 0) cJSON_AddItemToObject(filter, "until", cJSON_CreateNumber(until));
}
free(until_input);
// limit
char* limit_input = get_input("limit (max events)", "10");
if (limit_input && strlen(limit_input) > 0) {
int limit = atoi(limit_input);
if (limit > 0) cJSON_AddItemToObject(filter, "limit", cJSON_CreateNumber(limit));
}
free(limit_input);
// Get relay URLs from 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");
cJSON_Delete(filter);
return;
}
// Ask about close_on_eose behavior
char* close_input = get_input("Close subscription on EOSE? (y/n)", "n");
int close_on_eose = (close_input && strcmp(close_input, "y") == 0) ? 1 : 0;
free(close_input);
// Create subscription
nostr_pool_subscription_t* sub = nostr_relay_pool_subscribe(
pool,
(const char**)relay_urls,
relay_count,
filter,
on_event,
on_eose,
NULL,
close_on_eose
);
// Free relay URLs
for (int i = 0; i < relay_count; i++) {
free(relay_urls[i]);
}
free(relay_urls);
free(statuses);
if (!sub) {
printf("❌ Failed to create subscription\n");
cJSON_Delete(filter);
return;
}
// Store subscription
if (subscription_count >= subscription_capacity) {
subscription_capacity = subscription_capacity == 0 ? 10 : subscription_capacity * 2;
subscriptions = realloc(subscriptions, subscription_capacity * sizeof(nostr_pool_subscription_t*));
}
subscriptions[subscription_count++] = sub;
printf("✅ Subscription created (ID: %d)\n", subscription_count);
// Log the filter
char* filter_json = cJSON_Print(filter);
time_t now = time(NULL);
char timestamp[26];
ctime_r(&now, timestamp);
timestamp[24] = '\0';
dprintf(log_fd, "[%s] 🔍 New subscription created (ID: %d)\n", timestamp, subscription_count);
dprintf(log_fd, "Filter: %s\n\n", filter_json);
free(filter_json);
}
// Remove subscription
void remove_subscription() {
if (subscription_count == 0) {
printf("❌ No subscriptions to remove\n");
return;
}
printf("\n--- Remove Subscription ---\n");
printf("Available subscriptions:\n");
for (int i = 0; i < subscription_count; i++) {
printf("%d. Subscription %d\n", i + 1, i + 1);
}
char* choice_input = get_input("Enter subscription number to remove", NULL);
if (!choice_input || strlen(choice_input) == 0) {
free(choice_input);
return;
}
int choice = atoi(choice_input) - 1;
free(choice_input);
if (choice < 0 || choice >= subscription_count) {
printf("❌ Invalid subscription number\n");
return;
}
nostr_pool_subscription_close(subscriptions[choice]);
// Shift remaining subscriptions
for (int i = choice; i < subscription_count - 1; i++) {
subscriptions[i] = subscriptions[i + 1];
}
subscription_count--;
printf("✅ Subscription removed\n");
time_t now = time(NULL);
char timestamp[26];
ctime_r(&now, timestamp);
timestamp[24] = '\0';
dprintf(log_fd, "[%s] 🗑️ Subscription removed (was ID: %d)\n\n", timestamp, choice + 1);
}
// Show pool status
void show_pool_status() {
if (!pool) {
printf("❌ Pool not started\n");
return;
}
// Give polling thread time to establish connections
printf("⏳ Waiting for connections to establish...\n");
sleep(3);
char** relay_urls = NULL;
nostr_pool_relay_status_t* statuses = NULL;
int relay_count = nostr_relay_pool_list_relays(pool, &relay_urls, &statuses);
printf("\n📊 POOL STATUS\n");
printf("Relays: %d\n", relay_count);
printf("Subscriptions: %d\n", subscription_count);
if (relay_count > 0) {
printf("\nRelay Details:\n");
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);
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);
printf("│ ├── Ping latency: %.2f ms\n", stats->ping_latency_current);
printf("│ └── Query latency: %.2f ms\n", stats->query_latency_avg);
}
free(relay_urls[i]);
}
free(relay_urls);
free(statuses);
}
printf("\n");
}
int main() {
// Setup logging to file
log_fd = open("pool.log", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (log_fd == -1) {
fprintf(stderr, "❌ Failed to open pool.log for writing\n");
return 1;
}
// Initialize NOSTR library
if (nostr_init() != NOSTR_SUCCESS) {
fprintf(stderr, "❌ Failed to initialize NOSTR library\n");
close(log_fd);
return 1;
}
// Setup signal handler
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
// Start polling thread
if (pthread_create(&poll_thread, NULL, poll_thread_func, NULL) != 0) {
fprintf(stderr, "❌ Failed to create polling thread\n");
nostr_cleanup();
close(log_fd);
return 1;
}
printf("🔗 NOSTR Relay Pool Interactive Test\n");
printf("=====================================\n");
printf("All event output is logged to pool.log\n");
printf("Press Ctrl+C to exit\n\n");
time_t now = time(NULL);
char timestamp[26];
ctime_r(&now, timestamp);
timestamp[24] = '\0';
dprintf(log_fd, "[%s] 🚀 Pool test started\n\n", timestamp);
// Main menu loop
while (running) {
print_menu();
char choice;
if (scanf("%c", &choice) != 1) {
break;
}
// Consume newline
int c;
while ((c = getchar()) != '\n' && c != EOF);
switch (choice) {
case '1': { // Start Pool
if (pool) {
printf("❌ Pool already started\n");
break;
}
// Create pool with custom reconnection configuration for faster testing
nostr_pool_reconnect_config_t* config = nostr_pool_reconnect_config_default();
config->ping_interval_seconds = 5; // Ping every 5 seconds for testing
pool = nostr_relay_pool_create(config);
if (!pool) {
printf("❌ Failed to create pool\n");
break;
}
if (nostr_relay_pool_add_relay(pool, "wss://relay.laantungir.net") != NOSTR_SUCCESS) {
printf("❌ Failed to add default relay\n");
nostr_relay_pool_destroy(pool);
pool = NULL;
break;
}
printf("✅ Pool started with wss://relay.laantungir.net\n");
now = time(NULL);
ctime_r(&now, timestamp);
timestamp[24] = '\0';
dprintf(log_fd, "[%s] 🏊 Pool started with default relay\n\n", timestamp);
break;
}
case '2': { // Stop Pool
if (!pool) {
printf("❌ Pool not started\n");
break;
}
// Close all subscriptions
for (int i = 0; i < subscription_count; i++) {
if (subscriptions[i]) {
nostr_pool_subscription_close(subscriptions[i]);
}
}
free(subscriptions);
subscriptions = NULL;
subscription_count = 0;
subscription_capacity = 0;
nostr_relay_pool_destroy(pool);
pool = NULL;
printf("✅ Pool stopped\n");
now = time(NULL);
ctime_r(&now, timestamp);
timestamp[24] = '\0';
dprintf(log_fd, "[%s] 🛑 Pool stopped\n\n", timestamp);
break;
}
case '3': { // Add relay
if (!pool) {
printf("❌ Pool not started\n");
break;
}
char* url = get_input("Enter relay URL", "wss://relay.example.com");
if (url && strlen(url) > 0) {
if (nostr_relay_pool_add_relay(pool, url) == NOSTR_SUCCESS) {
printf("✅ Relay added: %s\n", url);
now = time(NULL);
ctime_r(&now, timestamp);
timestamp[24] = '\0';
dprintf(log_fd, "[%s] Relay added: %s\n\n", timestamp, url);
} else {
printf("❌ Failed to add relay\n");
}
}
free(url);
break;
}
case '4': { // Remove relay
if (!pool) {
printf("❌ Pool not started\n");
break;
}
char* url = get_input("Enter relay URL to remove", NULL);
if (url && strlen(url) > 0) {
if (nostr_relay_pool_remove_relay(pool, url) == NOSTR_SUCCESS) {
printf("✅ Relay removed: %s\n", url);
now = time(NULL);
ctime_r(&now, timestamp);
timestamp[24] = '\0';
dprintf(log_fd, "[%s] Relay removed: %s\n\n", timestamp, url);
} else {
printf("❌ Failed to remove relay\n");
}
}
free(url);
break;
}
case '5': // Add subscription
add_subscription();
break;
case '6': // Remove subscription
remove_subscription();
break;
case '7': // Show status
show_pool_status();
break;
case '8': { // Test reconnection
if (!pool) {
printf("❌ Pool not started\n");
break;
}
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");
break;
}
printf("\n--- Test Reconnection ---\n");
printf("Available relays:\n");
for (int i = 0; i < relay_count; i++) {
printf("%d. %s (%s)\n", i + 1, relay_urls[i],
statuses[i] == NOSTR_POOL_RELAY_CONNECTED ? "CONNECTED" : "NOT CONNECTED");
}
char* choice_input = get_input("Enter relay number to test reconnection with", NULL);
if (!choice_input || strlen(choice_input) == 0) {
for (int i = 0; i < relay_count; i++) free(relay_urls[i]);
free(relay_urls);
free(statuses);
free(choice_input);
break;
}
int choice = atoi(choice_input) - 1;
free(choice_input);
if (choice < 0 || choice >= relay_count) {
printf("❌ Invalid relay number\n");
for (int i = 0; i < relay_count; i++) free(relay_urls[i]);
free(relay_urls);
free(statuses);
break;
}
printf("🔄 Testing reconnection with %s...\n", relay_urls[choice]);
printf(" The pool is configured with automatic reconnection enabled.\n");
printf(" If the connection drops, it will automatically attempt to reconnect\n");
printf(" with exponential backoff (1s → 2s → 4s → 8s → 16s → 30s max).\n");
printf(" Connection health is monitored with ping/pong every 30 seconds.\n");
time_t now = time(NULL);
char timestamp[26];
ctime_r(&now, timestamp);
timestamp[24] = '\0';
dprintf(log_fd, "[%s] 🔄 TEST: Testing reconnection behavior with %s\n", timestamp, relay_urls[choice]);
dprintf(log_fd, " Pool configured with: auto-reconnect=ON, max_attempts=10, ping_interval=30s\n\n");
printf("✅ Reconnection test initiated. Monitor the status and logs for reconnection activity.\n");
for (int i = 0; i < relay_count; i++) free(relay_urls[i]);
free(relay_urls);
free(statuses);
break;
}
case '9': // Exit
running = 0;
break;
default:
printf("❌ Invalid choice\n");
break;
}
}
printf("\n🧹 Cleaning up...\n");
// Stop polling thread
running = 0;
pthread_join(poll_thread, NULL);
// Clean up pool and subscriptions
if (pool) {
for (int i = 0; i < subscription_count; i++) {
if (subscriptions[i]) {
nostr_pool_subscription_close(subscriptions[i]);
}
}
free(subscriptions);
nostr_relay_pool_destroy(pool);
printf("✅ Pool destroyed\n");
}
// Cleanup
nostr_cleanup();
close(log_fd);
printf("👋 Test completed\n");
return 0;
}

BIN
tests/relay_synchronous_test Executable file

Binary file not shown.

View File

@@ -0,0 +1,242 @@
/*
* 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(void* user_data) {
(void)user_data; // Suppress unused parameter warning
printf("📋 EOSE received - all stored events delivered\n");
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
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)
);
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;
}

Binary file not shown.

View File

@@ -10,7 +10,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "../nostr_core/nostr_common.h"
#include "../nostr_core/nostr_core.h"
#include "../cjson/cJSON.h"
@@ -80,7 +80,7 @@ int main() {
const char* filter_json =
"{"
" \"kinds\": [1],"
" \"limit\": 1"
" \"limit\": 4"
"}";
// Alternative filter examples (comment out the one above, uncomment one below):
@@ -133,7 +133,8 @@ int main() {
cJSON** results = synchronous_query_relays_with_progress(
test_relays, relay_count, filter, test_mode,
&result_count, 5, progress_callback, NULL
&result_count, 5, progress_callback, NULL,
1, NULL // nip42_enabled = true, private_key = NULL (no auth)
);
time_t end_time = time(NULL);

Binary file not shown.

View File

@@ -1,103 +0,0 @@
/*
* WebSocket SSL Test - Test OpenSSL WebSocket implementation
* Connect to a NOSTR relay and fetch one type 1 event
*/
#include <stdio.h>
#include <stdlib.h>
#include "../cjson/cJSON.h"
#include "../nostr_core/nostr_common.h"
// Progress callback to show connection status
static void progress_callback(
const char* relay_url,
const char* status,
const char* event_id,
int events_received,
int total_relays,
int completed_relays,
void* user_data)
{
printf("Progress: %s - %s", relay_url ? relay_url : "Summary", status);
if (event_id) {
printf(" (Event: %.12s...)", event_id);
}
printf(" [%d/%d events, %d/%d relays]\n",
events_received, *(int*)user_data, completed_relays, total_relays);
}
int main() {
printf("WebSocket SSL Test - Testing OpenSSL WebSocket with NOSTR relay\n");
printf("================================================================\n");
// Initialize NOSTR library
if (nostr_init() != NOSTR_SUCCESS) {
printf("❌ Failed to initialize NOSTR library\n");
return 1;
}
printf("✅ NOSTR library initialized\n");
// Setup relay and filter
const char* relay_urls[] = {"wss://nostr.mom"};
int relay_count = 1;
// Create filter for type 1 events (text notes), limit to 1 event
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(1));
printf("📡 Connecting to %s...\n", relay_urls[0]);
printf("🔍 Requesting 1 type 1 event (text note)...\n\n");
// Query the relay
int result_count = 0;
int expected_events = 1;
cJSON** events = synchronous_query_relays_with_progress(
relay_urls,
relay_count,
filter,
RELAY_QUERY_FIRST_RESULT, // Return as soon as we get the first event
&result_count,
10, // 10 second timeout
progress_callback,
&expected_events
);
// Process results
if (events && result_count > 0) {
printf("\n✅ Successfully received %d event(s)!\n", result_count);
printf("📄 Raw JSON event data:\n");
printf("========================\n");
for (int i = 0; i < result_count; i++) {
char* json_string = cJSON_Print(events[i]);
if (json_string) {
printf("%s\n\n", json_string);
free(json_string);
}
cJSON_Delete(events[i]);
}
free(events);
printf("🎉 WebSocket SSL Test PASSED - OpenSSL WebSocket working correctly!\n");
} else {
printf("\n❌ No events received or query failed\n");
printf("❌ WebSocket SSL Test FAILED\n");
// Cleanup and return error
cJSON_Delete(filter);
nostr_cleanup();
return 1;
}
// Cleanup
cJSON_Delete(filter);
nostr_cleanup();
printf("✅ WebSocket connection and TLS working with OpenSSL\n");
return 0;
}