Refactor and consolidate test suite
- Moved old tests from tests/old/ to main tests/ directory - Renamed nostr_test_bip32.c to bip32_test.c for consistency - Renamed nostr_crypto_test.c to crypto_test.c for consistency - Renamed wss_test.c and moved from old directory - Fixed unused variable warning in bip32_test.c - Updated build system and workspace rules - Cleaned up old compiled test executables - Updated nostr_common.h and core_relays.c - Removed obsolete nostr_core.h.old backup file This consolidates all active tests into the main directory and removes outdated test files while maintaining a clean, organized structure.
This commit is contained in:
@@ -1,905 +0,0 @@
|
||||
/*
|
||||
* NOSTR Core Library
|
||||
*
|
||||
* A C library for NOSTR protocol implementation
|
||||
* Self-contained crypto implementation (no external crypto dependencies)
|
||||
*
|
||||
* Features:
|
||||
* - BIP39 mnemonic generation and validation
|
||||
* - BIP32 hierarchical deterministic key derivation (NIP-06 compliant)
|
||||
* - NOSTR key pair generation and management
|
||||
* - Event creation, signing, and serialization
|
||||
* - Relay communication (websocket-based)
|
||||
* - Identity management and persistence
|
||||
*/
|
||||
|
||||
#ifndef NOSTR_CORE_H
|
||||
#define NOSTR_CORE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
// Forward declare cJSON to avoid requiring cJSON.h in public header
|
||||
typedef struct cJSON cJSON;
|
||||
|
||||
// Return codes
|
||||
#define NOSTR_SUCCESS 0
|
||||
#define NOSTR_ERROR_INVALID_INPUT -1
|
||||
#define NOSTR_ERROR_CRYPTO_FAILED -2
|
||||
#define NOSTR_ERROR_MEMORY_FAILED -3
|
||||
#define NOSTR_ERROR_IO_FAILED -4
|
||||
#define NOSTR_ERROR_NETWORK_FAILED -5
|
||||
#define NOSTR_ERROR_NIP04_INVALID_FORMAT -10
|
||||
#define NOSTR_ERROR_NIP04_DECRYPT_FAILED -11
|
||||
#define NOSTR_ERROR_NIP04_BUFFER_TOO_SMALL -12
|
||||
#define NOSTR_ERROR_NIP44_INVALID_FORMAT -13
|
||||
#define NOSTR_ERROR_NIP44_DECRYPT_FAILED -14
|
||||
#define NOSTR_ERROR_NIP44_BUFFER_TOO_SMALL -15
|
||||
#define NOSTR_ERROR_NIP05_INVALID_IDENTIFIER -16
|
||||
#define NOSTR_ERROR_NIP05_HTTP_FAILED -17
|
||||
#define NOSTR_ERROR_NIP05_JSON_PARSE_FAILED -18
|
||||
#define NOSTR_ERROR_NIP05_NAME_NOT_FOUND -19
|
||||
#define NOSTR_ERROR_NIP05_PUBKEY_MISMATCH -20
|
||||
|
||||
// Debug control - uncomment to enable debug output
|
||||
// #define NOSTR_DEBUG_ENABLED
|
||||
|
||||
// Constants
|
||||
#define NOSTR_PRIVATE_KEY_SIZE 32
|
||||
#define NOSTR_PUBLIC_KEY_SIZE 32
|
||||
#define NOSTR_HEX_KEY_SIZE 65 // 64 + null terminator
|
||||
#define NOSTR_BECH32_KEY_SIZE 100
|
||||
#define NOSTR_MAX_CONTENT_SIZE 2048
|
||||
#define NOSTR_MAX_URL_SIZE 256
|
||||
|
||||
// NIP-04 Constants
|
||||
#define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 16777216 // 16MB
|
||||
#define NOSTR_NIP04_MAX_ENCRYPTED_SIZE 22369621 // ~21.3MB (accounts for base64 overhead + IV)
|
||||
|
||||
// Input type detection
|
||||
typedef enum {
|
||||
NOSTR_INPUT_UNKNOWN = 0,
|
||||
NOSTR_INPUT_MNEMONIC,
|
||||
NOSTR_INPUT_NSEC_HEX,
|
||||
NOSTR_INPUT_NSEC_BECH32
|
||||
} nostr_input_type_t;
|
||||
|
||||
// Relay permissions
|
||||
typedef enum {
|
||||
NOSTR_RELAY_READ_WRITE = 0,
|
||||
NOSTR_RELAY_READ_ONLY,
|
||||
NOSTR_RELAY_WRITE_ONLY
|
||||
} nostr_relay_permission_t;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// LIBRARY MAINTENANCE - KEEP THE SHELVES NICE AND ORGANIZED.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* Initialize the NOSTR core library (must be called before using other functions)
|
||||
*
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_init(void);
|
||||
|
||||
/**
|
||||
* Cleanup the NOSTR core library (call when done)
|
||||
*/
|
||||
void nostr_cleanup(void);
|
||||
|
||||
/**
|
||||
* Get human-readable error message for error code
|
||||
*
|
||||
* @param error_code Error code from other functions
|
||||
* @return Human-readable error string
|
||||
*/
|
||||
const char* nostr_strerror(int error_code);
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GENERAL NOSTR UTILITIES
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Convert bytes to hexadecimal string
|
||||
*
|
||||
* @param bytes Input bytes
|
||||
* @param len Number of bytes
|
||||
* @param hex Output hex string (must be at least len*2+1 bytes)
|
||||
*/
|
||||
void nostr_bytes_to_hex(const unsigned char* bytes, size_t len, char* hex);
|
||||
|
||||
/**
|
||||
* Convert hexadecimal string to bytes
|
||||
*
|
||||
* @param hex Input hex string
|
||||
* @param bytes Output bytes buffer
|
||||
* @param len Expected number of bytes
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_hex_to_bytes(const char* hex, unsigned char* bytes, size_t len);
|
||||
|
||||
/**
|
||||
* Generate public key from private key
|
||||
*
|
||||
* @param private_key Input private key (32 bytes)
|
||||
* @param public_key Output public key (32 bytes, x-only)
|
||||
* @return 0 on success, non-zero on failure
|
||||
*/
|
||||
int nostr_ec_public_key_from_private_key(const unsigned char* private_key, unsigned char* public_key);
|
||||
|
||||
/**
|
||||
* Sign a hash using BIP-340 Schnorr signatures (NOSTR standard)
|
||||
*
|
||||
* @param private_key Input private key (32 bytes)
|
||||
* @param hash Input hash to sign (32 bytes)
|
||||
* @param signature Output signature (64 bytes)
|
||||
* @return 0 on success, non-zero on failure
|
||||
*/
|
||||
int nostr_schnorr_sign(const unsigned char* private_key, const unsigned char* hash, unsigned char* signature);
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NIP-01: BASIC PROTOCOL FLOW
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Create and sign a NOSTR event
|
||||
*
|
||||
* @param kind Event kind (0=profile, 1=text, 3=contacts, 10002=relays, etc.)
|
||||
* @param content Event content string
|
||||
* @param tags cJSON array of tags (NULL for empty tags)
|
||||
* @param private_key Private key for signing (32 bytes)
|
||||
* @param timestamp Event timestamp (0 for current time)
|
||||
* @return cJSON event object (caller must free), NULL on failure
|
||||
*/
|
||||
cJSON* nostr_create_and_sign_event(int kind, const char* content, cJSON* tags, const unsigned char* private_key, time_t timestamp);
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NIP-05: MAPPING NOSTR KEYS TO DNS-BASED INTERNET IDENTIFIERS
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Verify a NIP-05 identifier against a public key
|
||||
* Checks if the given identifier (e.g., "bob@example.com") maps to the provided pubkey
|
||||
*
|
||||
* @param nip05_identifier Internet identifier (e.g., "bob@example.com")
|
||||
* @param pubkey_hex Public key in hex format to verify against
|
||||
* @param relays OUTPUT: Array of relay URLs (caller must free each string and array), NULL if not needed
|
||||
* @param relay_count OUTPUT: Number of relay URLs returned, NULL if not needed
|
||||
* @param timeout_seconds HTTP timeout in seconds (0 for default 10 seconds)
|
||||
* @return NOSTR_SUCCESS if verified, NOSTR_ERROR_* on failure
|
||||
*/
|
||||
int nostr_nip05_verify(const char* nip05_identifier, const char* pubkey_hex,
|
||||
char*** relays, int* relay_count, int timeout_seconds);
|
||||
|
||||
/**
|
||||
* Lookup a public key from a NIP-05 identifier
|
||||
* Finds the public key associated with the given identifier (e.g., "bob@example.com")
|
||||
*
|
||||
* @param nip05_identifier Internet identifier (e.g., "bob@example.com")
|
||||
* @param pubkey_hex_out OUTPUT: Public key in hex format (65 bytes including null terminator)
|
||||
* @param relays OUTPUT: Array of relay URLs (caller must free each string and array), NULL if not needed
|
||||
* @param relay_count OUTPUT: Number of relay URLs returned, NULL if not needed
|
||||
* @param timeout_seconds HTTP timeout in seconds (0 for default 10 seconds)
|
||||
* @return NOSTR_SUCCESS if found, NOSTR_ERROR_* on failure
|
||||
*/
|
||||
int nostr_nip05_lookup(const char* nip05_identifier, char* pubkey_hex_out,
|
||||
char*** relays, int* relay_count, int timeout_seconds);
|
||||
|
||||
/**
|
||||
* Parse a .well-known/nostr.json response and extract pubkey and relays for a specific name
|
||||
*
|
||||
* @param json_response JSON string from .well-known/nostr.json endpoint
|
||||
* @param local_part Local part of identifier (e.g., "bob" from "bob@example.com")
|
||||
* @param pubkey_hex_out OUTPUT: Public key in hex format (65 bytes including null terminator)
|
||||
* @param relays OUTPUT: Array of relay URLs (caller must free each string and array), NULL if not needed
|
||||
* @param relay_count OUTPUT: Number of relay URLs returned, NULL if not needed
|
||||
* @return NOSTR_SUCCESS if found, NOSTR_ERROR_* on failure
|
||||
*/
|
||||
int nostr_nip05_parse_well_known(const char* json_response, const char* local_part,
|
||||
char* pubkey_hex_out, char*** relays, int* relay_count);
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NIP-11: RELAY INFORMATION DOCUMENT
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NIP-11 data structures
|
||||
typedef struct {
|
||||
char* name;
|
||||
char* description;
|
||||
char* pubkey;
|
||||
char* contact;
|
||||
int* supported_nips;
|
||||
size_t supported_nips_count;
|
||||
char* software;
|
||||
char* version;
|
||||
} nostr_relay_basic_info_t;
|
||||
|
||||
typedef struct {
|
||||
int max_message_length;
|
||||
int max_subscriptions;
|
||||
int max_filters;
|
||||
int max_limit;
|
||||
int max_subid_length;
|
||||
int min_prefix;
|
||||
int max_event_tags;
|
||||
int max_content_length;
|
||||
int min_pow_difficulty;
|
||||
int auth_required;
|
||||
int payment_required;
|
||||
long created_at_lower_limit;
|
||||
long created_at_upper_limit;
|
||||
int restricted_writes;
|
||||
} nostr_relay_limitations_t;
|
||||
|
||||
typedef struct {
|
||||
char** relay_countries;
|
||||
size_t relay_countries_count;
|
||||
} nostr_relay_content_limitations_t;
|
||||
|
||||
typedef struct {
|
||||
char** language_tags;
|
||||
size_t language_tags_count;
|
||||
char** tags;
|
||||
size_t tags_count;
|
||||
char* posting_policy;
|
||||
} nostr_relay_community_preferences_t;
|
||||
|
||||
typedef struct {
|
||||
char* icon;
|
||||
} nostr_relay_icon_t;
|
||||
|
||||
typedef struct {
|
||||
// Basic information (always present)
|
||||
nostr_relay_basic_info_t basic;
|
||||
|
||||
// Optional sections (check has_* flags)
|
||||
int has_limitations;
|
||||
nostr_relay_limitations_t limitations;
|
||||
|
||||
int has_content_limitations;
|
||||
nostr_relay_content_limitations_t content_limitations;
|
||||
|
||||
int has_community_preferences;
|
||||
nostr_relay_community_preferences_t community_preferences;
|
||||
|
||||
int has_icon;
|
||||
nostr_relay_icon_t icon;
|
||||
} nostr_relay_info_t;
|
||||
|
||||
/**
|
||||
* Fetch relay information document from a relay URL
|
||||
* Converts WebSocket URLs to HTTP and retrieves NIP-11 document
|
||||
*
|
||||
* @param relay_url Relay URL (ws://, wss://, http://, or https://)
|
||||
* @param info_out OUTPUT: Pointer to relay info structure (caller must free with nostr_nip11_relay_info_free)
|
||||
* @param timeout_seconds HTTP timeout in seconds (0 for default 10 seconds)
|
||||
* @return NOSTR_SUCCESS if retrieved, NOSTR_ERROR_* on failure
|
||||
*/
|
||||
int nostr_nip11_fetch_relay_info(const char* relay_url, nostr_relay_info_t** info_out, int timeout_seconds);
|
||||
|
||||
/**
|
||||
* Free relay information structure
|
||||
*
|
||||
* @param info Relay info structure to free (safe to pass NULL)
|
||||
*/
|
||||
void nostr_nip11_relay_info_free(nostr_relay_info_t* info);
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NIP-04: ENCRYPTED DIRECT MESSAGES
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Encrypt a message using NIP-04 (ECDH + AES-CBC + Base64)
|
||||
*
|
||||
* @param sender_private_key Sender's 32-byte private key
|
||||
* @param recipient_public_key Recipient's 32-byte public key (x-only)
|
||||
* @param plaintext Message to encrypt
|
||||
* @param output Buffer for encrypted result (recommend NOSTR_NIP04_MAX_ENCRYPTED_SIZE)
|
||||
* @param output_size Size of output buffer
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_nip04_encrypt(const unsigned char* sender_private_key,
|
||||
const unsigned char* recipient_public_key,
|
||||
const char* plaintext,
|
||||
char* output,
|
||||
size_t output_size);
|
||||
|
||||
/**
|
||||
* Decrypt a NIP-04 encrypted message
|
||||
*
|
||||
* @param recipient_private_key Recipient's 32-byte private key
|
||||
* @param sender_public_key Sender's 32-byte public key (x-only)
|
||||
* @param encrypted_data Encrypted message in format "ciphertext?iv=iv"
|
||||
* @param output Buffer for decrypted plaintext (recommend NOSTR_NIP04_MAX_PLAINTEXT_SIZE)
|
||||
* @param output_size Size of output buffer
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_nip04_decrypt(const unsigned char* recipient_private_key,
|
||||
const unsigned char* sender_public_key,
|
||||
const char* encrypted_data,
|
||||
char* output,
|
||||
size_t output_size);
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NIP-44: VERSIONED ENCRYPTED DIRECT MESSAGES
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Encrypt a message using NIP-44 v2 (ECDH + ChaCha20 + HMAC)
|
||||
*
|
||||
* @param sender_private_key Sender's 32-byte private key
|
||||
* @param recipient_public_key Recipient's 32-byte public key (x-only)
|
||||
* @param plaintext Message to encrypt
|
||||
* @param output Buffer for encrypted result (recommend NOSTR_NIP44_MAX_PLAINTEXT_SIZE * 2)
|
||||
* @param output_size Size of output buffer
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_nip44_encrypt(const unsigned char* sender_private_key,
|
||||
const unsigned char* recipient_public_key,
|
||||
const char* plaintext,
|
||||
char* output,
|
||||
size_t output_size);
|
||||
|
||||
/**
|
||||
* Decrypt a NIP-44 encrypted message
|
||||
*
|
||||
* @param recipient_private_key Recipient's 32-byte private key
|
||||
* @param sender_public_key Sender's 32-byte public key (x-only)
|
||||
* @param encrypted_data Base64-encoded encrypted message
|
||||
* @param output Buffer for decrypted plaintext
|
||||
* @param output_size Size of output buffer
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_nip44_decrypt(const unsigned char* recipient_private_key,
|
||||
const unsigned char* sender_public_key,
|
||||
const char* encrypted_data,
|
||||
char* output,
|
||||
size_t output_size);
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NIP-06: KEY DERIVATION FROM MNEMONIC
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Generate a random NOSTR keypair using cryptographically secure entropy
|
||||
*
|
||||
* @param private_key Output buffer for private key (32 bytes)
|
||||
* @param public_key Output buffer for public key (32 bytes, x-only)
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_generate_keypair(unsigned char* private_key, unsigned char* public_key);
|
||||
|
||||
|
||||
/**
|
||||
* Generate a BIP39 mnemonic phrase and derive NOSTR keys
|
||||
*
|
||||
* @param mnemonic Output buffer for mnemonic (at least 256 bytes recommended)
|
||||
* @param mnemonic_size Size of mnemonic buffer
|
||||
* @param account Account number for key derivation (default: 0)
|
||||
* @param private_key Output buffer for private key (32 bytes)
|
||||
* @param public_key Output buffer for public key (32 bytes)
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_generate_mnemonic_and_keys(char* mnemonic, size_t mnemonic_size,
|
||||
int account, unsigned char* private_key,
|
||||
unsigned char* public_key);
|
||||
|
||||
|
||||
/**
|
||||
* Derive NOSTR keys from existing BIP39 mnemonic (NIP-06 compliant)
|
||||
*
|
||||
* @param mnemonic BIP39 mnemonic phrase
|
||||
* @param account Account number for derivation path m/44'/1237'/account'/0/0
|
||||
* @param private_key Output buffer for private key (32 bytes)
|
||||
* @param public_key Output buffer for public key (32 bytes)
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_derive_keys_from_mnemonic(const char* mnemonic, int account,
|
||||
unsigned char* private_key, unsigned char* public_key);
|
||||
|
||||
|
||||
/**
|
||||
* Convert NOSTR key to bech32 format (nsec/npub)
|
||||
*
|
||||
* @param key Key data (32 bytes)
|
||||
* @param hrp Human readable part ("nsec" or "npub")
|
||||
* @param output Output buffer (at least 100 bytes recommended)
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_key_to_bech32(const unsigned char* key, const char* hrp, char* output);
|
||||
|
||||
|
||||
/**
|
||||
* Detect the type of input string (mnemonic, hex nsec, bech32 nsec)
|
||||
*
|
||||
* @param input Input string to analyze
|
||||
* @return Input type enum
|
||||
*/
|
||||
nostr_input_type_t nostr_detect_input_type(const char* input);
|
||||
|
||||
|
||||
/**
|
||||
* Validate and decode an nsec (hex or bech32) to private key
|
||||
*
|
||||
* @param input Input nsec string
|
||||
* @param private_key Output buffer for private key (32 bytes)
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_decode_nsec(const char* input, unsigned char* private_key);
|
||||
|
||||
/**
|
||||
* Validate and decode an npub (hex or bech32) to private key
|
||||
*
|
||||
* @param input Input nsec string
|
||||
* @param private_key Output buffer for private key (32 bytes)
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_decode_npub(const char* input, unsigned char* private_key);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NIP-13: PROOF OF WORK
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Add NIP-13 Proof of Work to an existing event
|
||||
*
|
||||
* @param event cJSON event object to add PoW to
|
||||
* @param private_key Private key for re-signing the event during mining
|
||||
* @param target_difficulty Target number of leading zero bits (default: 4 if 0)
|
||||
* @param max_attempts Maximum number of mining attempts (default: 10,000,000 if <= 0)
|
||||
* @param progress_report_interval How often to call progress callback (default: 10,000 if <= 0)
|
||||
* @param timestamp_update_interval How often to update timestamp (default: 10,000 if <= 0)
|
||||
* @param progress_callback Optional callback for progress updates (current_difficulty, nonce, user_data)
|
||||
* @param user_data User data passed to progress callback
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_add_proof_of_work(cJSON* event, const unsigned char* private_key,
|
||||
int target_difficulty, int max_attempts,
|
||||
int progress_report_interval, int timestamp_update_interval,
|
||||
void (*progress_callback)(int current_difficulty, uint64_t nonce, void* user_data),
|
||||
void* user_data);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// RELAYS - SYNCHRONOUS MULTI-RELAY QUERIES AND PUBLISHING
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Query a relay for a specific event
|
||||
*
|
||||
* @param relay_url Relay WebSocket URL (ws:// or wss://)
|
||||
* @param pubkey_hex Author's public key in hex format
|
||||
* @param kind Event kind to search for
|
||||
* @return cJSON event object (caller must free), NULL if not found/error
|
||||
*/
|
||||
cJSON* nostr_query_relay_for_event(const char* relay_url, const char* pubkey_hex, int kind);
|
||||
|
||||
|
||||
// Query mode enum
|
||||
typedef enum {
|
||||
RELAY_QUERY_FIRST_RESULT, // Return as soon as first event is found
|
||||
RELAY_QUERY_MOST_RECENT, // Wait for all relays, return most recent event
|
||||
RELAY_QUERY_ALL_RESULTS // Wait for all relays, return all unique events
|
||||
} relay_query_mode_t;
|
||||
|
||||
// Progress callback type for relay queries
|
||||
typedef void (*relay_progress_callback_t)(
|
||||
const char* relay_url, // Which relay is reporting (NULL for summary)
|
||||
const char* status, // Status: "connecting", "subscribed", "event_found", "eose", "complete", "timeout", "error", "first_result", "all_complete"
|
||||
const char* event_id, // Event ID when applicable (NULL otherwise)
|
||||
int events_received, // Number of events from this relay
|
||||
int total_relays, // Total number of relays
|
||||
int completed_relays, // Number of relays finished
|
||||
void* user_data // User data pointer
|
||||
);
|
||||
|
||||
/**
|
||||
* Query multiple relays synchronously with progress callbacks
|
||||
*
|
||||
* @param relay_urls Array of relay WebSocket URLs
|
||||
* @param relay_count Number of relays in array
|
||||
* @param filter cJSON filter object for query
|
||||
* @param mode Query mode (FIRST_RESULT, MOST_RECENT, or ALL_RESULTS)
|
||||
* @param result_count OUTPUT: number of events returned
|
||||
* @param relay_timeout_seconds Timeout per relay in seconds (default: 2 if <= 0)
|
||||
* @param callback Progress callback function (can be NULL)
|
||||
* @param user_data User data passed to callback
|
||||
* @return Array of cJSON events (caller must free each event and array), NULL on failure
|
||||
*/
|
||||
cJSON** synchronous_query_relays_with_progress(
|
||||
const char** relay_urls,
|
||||
int relay_count,
|
||||
cJSON* filter,
|
||||
relay_query_mode_t mode,
|
||||
int* result_count,
|
||||
int relay_timeout_seconds,
|
||||
relay_progress_callback_t callback,
|
||||
void* user_data
|
||||
);
|
||||
|
||||
// Publish result enum
|
||||
typedef enum {
|
||||
PUBLISH_SUCCESS, // Event accepted by relay (received OK with true)
|
||||
PUBLISH_REJECTED, // Event rejected by relay (received OK with false)
|
||||
PUBLISH_TIMEOUT, // No response from relay within timeout
|
||||
PUBLISH_ERROR // Connection error or other failure
|
||||
} publish_result_t;
|
||||
|
||||
// Progress callback type for publishing
|
||||
typedef void (*publish_progress_callback_t)(
|
||||
const char* relay_url, // Which relay is reporting
|
||||
const char* status, // Status: "connecting", "publishing", "accepted", "rejected", "timeout", "error"
|
||||
const char* message, // OK message from relay (for rejected events)
|
||||
int successful_publishes, // Count of successful publishes so far
|
||||
int total_relays, // Total number of relays
|
||||
int completed_relays, // Number of relays finished
|
||||
void* user_data // User data pointer
|
||||
);
|
||||
|
||||
/**
|
||||
* Publish event to multiple relays synchronously with progress callbacks
|
||||
*
|
||||
* @param relay_urls Array of relay WebSocket URLs
|
||||
* @param relay_count Number of relays in array
|
||||
* @param event cJSON event object to publish
|
||||
* @param success_count OUTPUT: number of successful publishes
|
||||
* @param relay_timeout_seconds Timeout per relay in seconds (default: 5 if <= 0)
|
||||
* @param callback Progress callback function (can be NULL)
|
||||
* @param user_data User data passed to callback
|
||||
* @return Array of publish_result_t (caller must free), NULL on failure
|
||||
*/
|
||||
publish_result_t* synchronous_publish_event_with_progress(
|
||||
const char** relay_urls,
|
||||
int relay_count,
|
||||
cJSON* event,
|
||||
int* success_count,
|
||||
int relay_timeout_seconds,
|
||||
publish_progress_callback_t callback,
|
||||
void* user_data
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// RELAYS - ASYNCHRONOUS RELAY POOLS
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Forward declarations for relay pool types
|
||||
typedef struct nostr_relay_pool nostr_relay_pool_t;
|
||||
typedef struct nostr_pool_subscription nostr_pool_subscription_t;
|
||||
|
||||
// Pool connection status
|
||||
typedef enum {
|
||||
NOSTR_POOL_RELAY_DISCONNECTED = 0,
|
||||
NOSTR_POOL_RELAY_CONNECTING = 1,
|
||||
NOSTR_POOL_RELAY_CONNECTED = 2,
|
||||
NOSTR_POOL_RELAY_ERROR = -1
|
||||
} nostr_pool_relay_status_t;
|
||||
|
||||
// Relay statistics structure
|
||||
typedef struct {
|
||||
// Event counters
|
||||
int events_received;
|
||||
int events_published;
|
||||
int events_published_ok;
|
||||
int events_published_failed;
|
||||
|
||||
// Connection stats
|
||||
int connection_attempts;
|
||||
int connection_failures;
|
||||
time_t connection_uptime_start;
|
||||
time_t last_event_time;
|
||||
|
||||
// Latency measurements (milliseconds)
|
||||
// NOTE: ping_latency_* values will be 0.0/-1.0 until PONG response handling is fixed
|
||||
double ping_latency_current;
|
||||
double ping_latency_avg;
|
||||
double ping_latency_min;
|
||||
double ping_latency_max;
|
||||
double publish_latency_avg; // EVENT->OK response time
|
||||
double query_latency_avg; // REQ->first EVENT response time
|
||||
double query_latency_min; // Min query latency
|
||||
double query_latency_max; // Max query latency
|
||||
|
||||
// Sample counts for averaging
|
||||
int ping_samples;
|
||||
int publish_samples;
|
||||
int query_samples;
|
||||
} nostr_relay_stats_t;
|
||||
|
||||
/**
|
||||
* Create a new relay pool for managing multiple relay connections
|
||||
*
|
||||
* @return New relay pool instance (caller must destroy), NULL on failure
|
||||
*/
|
||||
nostr_relay_pool_t* nostr_relay_pool_create(void);
|
||||
|
||||
/**
|
||||
* Add a relay to the pool
|
||||
*
|
||||
* @param pool Relay pool instance
|
||||
* @param relay_url Relay WebSocket URL (ws:// or wss://)
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_relay_pool_add_relay(nostr_relay_pool_t* pool, const char* relay_url);
|
||||
|
||||
/**
|
||||
* Remove a relay from the pool
|
||||
*
|
||||
* @param pool Relay pool instance
|
||||
* @param relay_url Relay URL to remove
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_relay_pool_remove_relay(nostr_relay_pool_t* pool, const char* relay_url);
|
||||
|
||||
/**
|
||||
* Destroy relay pool and cleanup all connections
|
||||
*
|
||||
* @param pool Relay pool instance to destroy
|
||||
*/
|
||||
void nostr_relay_pool_destroy(nostr_relay_pool_t* pool);
|
||||
|
||||
/**
|
||||
* Subscribe to events from multiple relays with event deduplication
|
||||
*
|
||||
* @param pool Relay pool instance
|
||||
* @param relay_urls Array of relay URLs to subscribe to
|
||||
* @param relay_count Number of relays in array
|
||||
* @param filter cJSON filter object for subscription
|
||||
* @param on_event Callback for received events (event, relay_url, user_data)
|
||||
* @param on_eose Callback when all relays have sent EOSE (user_data)
|
||||
* @param user_data User data passed to callbacks
|
||||
* @return Subscription handle (caller must close), NULL on failure
|
||||
*/
|
||||
nostr_pool_subscription_t* nostr_relay_pool_subscribe(
|
||||
nostr_relay_pool_t* pool,
|
||||
const char** relay_urls,
|
||||
int relay_count,
|
||||
cJSON* filter,
|
||||
void (*on_event)(cJSON* event, const char* relay_url, void* user_data),
|
||||
void (*on_eose)(void* user_data),
|
||||
void* user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* Close a pool subscription
|
||||
*
|
||||
* @param subscription Subscription to close
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_pool_subscription_close(nostr_pool_subscription_t* subscription);
|
||||
|
||||
/**
|
||||
* Query multiple relays synchronously and return all matching events
|
||||
*
|
||||
* @param pool Relay pool instance
|
||||
* @param relay_urls Array of relay URLs to query
|
||||
* @param relay_count Number of relays in array
|
||||
* @param filter cJSON filter object for query
|
||||
* @param event_count Output: number of events returned
|
||||
* @param timeout_ms Timeout in milliseconds
|
||||
* @return Array of cJSON events (caller must free), NULL on failure/timeout
|
||||
*/
|
||||
cJSON** nostr_relay_pool_query_sync(
|
||||
nostr_relay_pool_t* pool,
|
||||
const char** relay_urls,
|
||||
int relay_count,
|
||||
cJSON* filter,
|
||||
int* event_count,
|
||||
int timeout_ms
|
||||
);
|
||||
|
||||
/**
|
||||
* Get a single event from multiple relays (returns the most recent one)
|
||||
*
|
||||
* @param pool Relay pool instance
|
||||
* @param relay_urls Array of relay URLs to query
|
||||
* @param relay_count Number of relays in array
|
||||
* @param filter cJSON filter object for query
|
||||
* @param timeout_ms Timeout in milliseconds
|
||||
* @return cJSON event (caller must free), NULL if not found/timeout
|
||||
*/
|
||||
cJSON* nostr_relay_pool_get_event(
|
||||
nostr_relay_pool_t* pool,
|
||||
const char** relay_urls,
|
||||
int relay_count,
|
||||
cJSON* filter,
|
||||
int timeout_ms
|
||||
);
|
||||
|
||||
/**
|
||||
* Publish an event to multiple relays
|
||||
*
|
||||
* @param pool Relay pool instance
|
||||
* @param relay_urls Array of relay URLs to publish to
|
||||
* @param relay_count Number of relays in array
|
||||
* @param event cJSON event to publish
|
||||
* @return Number of successful publishes, negative on error
|
||||
*/
|
||||
int nostr_relay_pool_publish(
|
||||
nostr_relay_pool_t* pool,
|
||||
const char** relay_urls,
|
||||
int relay_count,
|
||||
cJSON* event
|
||||
);
|
||||
|
||||
/**
|
||||
* Get connection status for a relay in the pool
|
||||
*
|
||||
* @param pool Relay pool instance
|
||||
* @param relay_url Relay URL to check
|
||||
* @return Connection status enum
|
||||
*/
|
||||
nostr_pool_relay_status_t nostr_relay_pool_get_relay_status(
|
||||
nostr_relay_pool_t* pool,
|
||||
const char* relay_url
|
||||
);
|
||||
|
||||
/**
|
||||
* Get list of all relays in pool with their status
|
||||
*
|
||||
* @param pool Relay pool instance
|
||||
* @param relay_urls Output: array of relay URL strings (caller must free)
|
||||
* @param statuses Output: array of status values (caller must free)
|
||||
* @return Number of relays, negative on error
|
||||
*/
|
||||
int nostr_relay_pool_list_relays(
|
||||
nostr_relay_pool_t* pool,
|
||||
char*** relay_urls,
|
||||
nostr_pool_relay_status_t** statuses
|
||||
);
|
||||
|
||||
/**
|
||||
* Run continuous event processing for active subscriptions (blocking)
|
||||
* Processes incoming events and calls subscription callbacks until timeout or stopped
|
||||
*
|
||||
* @param pool Relay pool instance
|
||||
* @param timeout_ms Timeout in milliseconds (0 = no timeout, runs indefinitely)
|
||||
* @return Total number of events processed, negative on error
|
||||
*/
|
||||
int nostr_relay_pool_run(nostr_relay_pool_t* pool, int timeout_ms);
|
||||
|
||||
/**
|
||||
* Process events for active subscriptions (non-blocking, single pass)
|
||||
* Processes available events and returns immediately
|
||||
*
|
||||
* @param pool Relay pool instance
|
||||
* @param timeout_ms Maximum time to spend processing in milliseconds
|
||||
* @return Number of events processed in this call, negative on error
|
||||
*/
|
||||
int nostr_relay_pool_poll(nostr_relay_pool_t* pool, int timeout_ms);
|
||||
|
||||
// =============================================================================
|
||||
// RELAY POOL STATISTICS AND LATENCY
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Get statistics for a specific relay in the pool
|
||||
*
|
||||
* @param pool Relay pool instance
|
||||
* @param relay_url Relay URL to get statistics for
|
||||
* @return Pointer to statistics structure (owned by pool), NULL if relay not found
|
||||
*/
|
||||
const nostr_relay_stats_t* nostr_relay_pool_get_relay_stats(
|
||||
nostr_relay_pool_t* pool,
|
||||
const char* relay_url
|
||||
);
|
||||
|
||||
/**
|
||||
* Reset statistics for a specific relay
|
||||
*
|
||||
* @param pool Relay pool instance
|
||||
* @param relay_url Relay URL to reset statistics for
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_relay_pool_reset_relay_stats(
|
||||
nostr_relay_pool_t* pool,
|
||||
const char* relay_url
|
||||
);
|
||||
|
||||
/**
|
||||
* Get current ping latency for a relay (most recent ping result)
|
||||
*
|
||||
* @param pool Relay pool instance
|
||||
* @param relay_url Relay URL to check
|
||||
* @return Ping latency in milliseconds, -1.0 if no ping data available
|
||||
*/
|
||||
double nostr_relay_pool_get_relay_ping_latency(
|
||||
nostr_relay_pool_t* pool,
|
||||
const char* relay_url
|
||||
);
|
||||
|
||||
/**
|
||||
* Get average query latency for a relay (REQ->first EVENT response time)
|
||||
*
|
||||
* @param pool Relay pool instance
|
||||
* @param relay_url Relay URL to check
|
||||
* @return Average query latency in milliseconds, -1.0 if no data available
|
||||
*/
|
||||
double nostr_relay_pool_get_relay_query_latency(
|
||||
nostr_relay_pool_t* pool,
|
||||
const char* relay_url
|
||||
);
|
||||
|
||||
/**
|
||||
* Manually trigger ping measurement for a relay (asynchronous)
|
||||
*
|
||||
* NOTE: PING frames are sent correctly, but PONG response handling needs debugging.
|
||||
* Currently times out waiting for PONG responses. Future fix needed.
|
||||
*
|
||||
* @param pool Relay pool instance
|
||||
* @param relay_url Relay URL to ping
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_relay_pool_ping_relay(
|
||||
nostr_relay_pool_t* pool,
|
||||
const char* relay_url
|
||||
);
|
||||
|
||||
/**
|
||||
* Manually trigger ping measurement for a relay and wait for response (synchronous)
|
||||
*
|
||||
* NOTE: PING frames are sent correctly, but PONG response handling needs debugging.
|
||||
* Currently times out waiting for PONG responses. Future fix needed.
|
||||
*
|
||||
* @param pool Relay pool instance
|
||||
* @param relay_url Relay URL to ping
|
||||
* @param timeout_seconds Timeout in seconds (0 for default 5 seconds)
|
||||
* @return NOSTR_SUCCESS on success, error code on failure
|
||||
*/
|
||||
int nostr_relay_pool_ping_relay_sync(
|
||||
nostr_relay_pool_t* pool,
|
||||
const char* relay_url,
|
||||
int timeout_seconds
|
||||
);
|
||||
|
||||
#endif // NOSTR_CORE_H
|
||||
Reference in New Issue
Block a user