Completed refactoring to separate nip files, and updating build.sh

This commit is contained in:
2025-08-16 07:42:48 -04:00
parent 8ed9262c65
commit c3a9482882
37 changed files with 2693 additions and 3578 deletions

Binary file not shown.

View File

@@ -1,348 +0,0 @@
/*
* Makefile-Based Static Linking Test
*
* This test validates static linking configuration by parsing the Makefile
* instead of analyzing compiled binaries. This approach is faster, more reliable,
* and catches configuration issues at the source.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// Test result tracking
static int tests_run = 0;
static int tests_passed = 0;
// Test macros
#define ASSERT(condition, message) \
do { \
tests_run++; \
if (condition) { \
printf("✓ %s: PASSED\n", message); \
tests_passed++; \
} else { \
printf("✗ %s: FAILED\n", message); \
} \
} while(0)
#define ASSERT_CONTAINS(haystack, needle, message) \
ASSERT(strstr(haystack, needle) != NULL, message)
#define ASSERT_NOT_CONTAINS(haystack, needle, message) \
ASSERT(strstr(haystack, needle) == NULL, message)
// File reading utility
char* read_file(const char* filename) {
FILE* file = fopen(filename, "r");
if (!file) {
printf("ERROR: Cannot open %s\n", filename);
return NULL;
}
fseek(file, 0, SEEK_END);
long length = ftell(file);
fseek(file, 0, SEEK_SET);
char* content = malloc(length + 1);
if (!content) {
fclose(file);
return NULL;
}
fread(content, 1, length, file);
content[length] = '\0';
fclose(file);
return content;
}
// Extract variable value from Makefile
char* extract_makefile_variable(const char* content, const char* variable) {
char search_pattern[256];
snprintf(search_pattern, sizeof(search_pattern), "%s =", variable);
char* line_start = strstr(content, search_pattern);
if (!line_start) {
snprintf(search_pattern, sizeof(search_pattern), "%s=", variable);
line_start = strstr(content, search_pattern);
}
if (!line_start) return NULL;
// Find start of value (after '=')
char* value_start = strchr(line_start, '=');
if (!value_start) return NULL;
value_start++; // Skip '='
// Skip whitespace
while (*value_start == ' ' || *value_start == '\t') {
value_start++;
}
// Find end of line
char* line_end = strchr(value_start, '\n');
if (!line_end) {
line_end = value_start + strlen(value_start);
}
// Handle line continuations with backslash
char* result = malloc(2048);
char* result_ptr = result;
char* current = value_start;
while (current < line_end) {
if (*current == '\\' && (current + 1) < line_end && *(current + 1) == '\n') {
// Line continuation - skip to next line
current += 2; // Skip '\\\n'
// Find next non-whitespace
while (current < line_end && (*current == ' ' || *current == '\t')) {
current++;
}
*result_ptr++ = ' '; // Add space between continued lines
} else {
*result_ptr++ = *current++;
}
}
*result_ptr = '\0';
// Trim trailing whitespace
result_ptr--;
while (result_ptr > result && (*result_ptr == ' ' || *result_ptr == '\t')) {
*result_ptr-- = '\0';
}
return result;
}
// Test static linking configuration
void test_static_linking_flags(const char* makefile_content) {
printf("\n=== Static Linking Flags Test ===\n");
// Check TEST_LDFLAGS contains -static
char* test_ldflags = extract_makefile_variable(makefile_content, "TEST_LDFLAGS");
if (test_ldflags) {
ASSERT_CONTAINS(test_ldflags, "-static", "TEST_LDFLAGS contains -static flag");
free(test_ldflags);
} else {
ASSERT(0, "TEST_LDFLAGS variable found in Makefile");
}
// Check ARM64_TEST_LDFLAGS contains -static
char* arm64_test_ldflags = extract_makefile_variable(makefile_content, "ARM64_TEST_LDFLAGS");
if (arm64_test_ldflags) {
ASSERT_CONTAINS(arm64_test_ldflags, "-static", "ARM64_TEST_LDFLAGS contains -static flag");
free(arm64_test_ldflags);
} else {
ASSERT(0, "ARM64_TEST_LDFLAGS variable found in Makefile");
}
}
// Test forbidden dynamic library links
void test_forbidden_dynamic_links(const char* makefile_content) {
printf("\n=== Forbidden Dynamic Links Test ===\n");
// List of libraries that should not be dynamically linked in core tests
const char* forbidden_libs[] = {
"-lsecp256k1", // Should be statically included
"-lsodium", // Not used
"-lwally", // Not used
"-lmbedtls", // Replaced with OpenSSL
"-lmbedx509", // Replaced with OpenSSL
"-lmbedcrypto", // Replaced with OpenSSL
NULL
};
for (int i = 0; forbidden_libs[i] != NULL; i++) {
// Check that forbidden library is not in TEST_LDFLAGS
char* test_ldflags = extract_makefile_variable(makefile_content, "TEST_LDFLAGS");
if (test_ldflags) {
char message[256];
snprintf(message, sizeof(message), "TEST_LDFLAGS does not contain %s", forbidden_libs[i]);
ASSERT_NOT_CONTAINS(test_ldflags, forbidden_libs[i], message);
free(test_ldflags);
}
}
}
// Test that we use the static library
void test_static_library_usage(const char* makefile_content) {
printf("\n=== Static Library Usage Test ===\n");
// Check that TEST_LDFLAGS links against our static library
char* test_ldflags = extract_makefile_variable(makefile_content, "TEST_LDFLAGS");
if (test_ldflags) {
ASSERT_CONTAINS(test_ldflags, "-lnostr_core", "TEST_LDFLAGS links against libnostr_core");
ASSERT_CONTAINS(test_ldflags, "-lm", "TEST_LDFLAGS links against math library");
free(test_ldflags);
}
// Check ARM64 version
char* arm64_test_ldflags = extract_makefile_variable(makefile_content, "ARM64_TEST_LDFLAGS");
if (arm64_test_ldflags) {
ASSERT_CONTAINS(arm64_test_ldflags, "-lnostr_core_arm64", "ARM64_TEST_LDFLAGS links against ARM64 static library");
free(arm64_test_ldflags);
}
}
// Test that compilation flags disable problematic features for static tests only
void test_compilation_flags(const char* makefile_content) {
printf("\n=== Compilation Flags Test ===\n");
// Check TEST_CFLAGS contains DISABLE_NIP05 (static tests need to avoid curl)
char* test_cflags = extract_makefile_variable(makefile_content, "TEST_CFLAGS");
if (test_cflags) {
ASSERT_CONTAINS(test_cflags, "-DDISABLE_NIP05", "TEST_CFLAGS disables NIP-05 to avoid curl dependency");
free(test_cflags);
}
// Check that main compilation does NOT disable NIP05 (NIP-05 should be enabled by default)
// Look for DISABLE_NIP05 in object file compilation rules - should NOT be there
char* obj_rule_start = strstr(makefile_content, "%.o: %.c");
if (obj_rule_start) {
char* obj_rule_end = strstr(obj_rule_start, "\n\n");
if (!obj_rule_end) obj_rule_end = makefile_content + strlen(makefile_content);
char* obj_rule = malloc(obj_rule_end - obj_rule_start + 1);
strncpy(obj_rule, obj_rule_start, obj_rule_end - obj_rule_start);
obj_rule[obj_rule_end - obj_rule_start] = '\0';
ASSERT_NOT_CONTAINS(obj_rule, "-DDISABLE_NIP05", "Main compilation does not disable NIP-05");
free(obj_rule);
}
}
// Test static curl usage (no dynamic -lcurl)
void test_static_curl_usage(const char* makefile_content) {
printf("\n=== Static Curl Usage Test ===\n");
// Count occurrences of -lcurl (should be zero - we use static libcurl.a)
int curl_count = 0;
const char* search_pos = makefile_content;
while ((search_pos = strstr(search_pos, "-lcurl")) != NULL) {
curl_count++;
search_pos += 6; // Move past "-lcurl"
}
char message[256];
snprintf(message, sizeof(message), "No dynamic curl usage found (found %d -lcurl occurrences)", curl_count);
ASSERT(curl_count == 0, message);
// Verify HTTP and NIP-05 tests use static libcurl.a instead
ASSERT_CONTAINS(makefile_content, "./curl-install/lib/libcurl.a", "Static libcurl.a is used");
// Verify curl include path is used
ASSERT_CONTAINS(makefile_content, "-I./curl-install/include", "Curl include path is used");
// Verify both HTTP and NIP-05 tests use static linking
char* http_test_line = strstr(makefile_content, "$(HTTP_TEST_EXEC): tests/http_test.c");
if (http_test_line) {
char* next_rule = strstr(http_test_line, "\n\n");
if (!next_rule) next_rule = makefile_content + strlen(makefile_content);
char* http_section = malloc(next_rule - http_test_line + 1);
strncpy(http_section, http_test_line, next_rule - http_test_line);
http_section[next_rule - http_test_line] = '\0';
ASSERT_CONTAINS(http_section, "./curl-install/lib/libcurl.a", "HTTP test uses static libcurl.a");
ASSERT_CONTAINS(http_section, "-static", "HTTP test uses static linking");
free(http_section);
}
char* nip05_test_line = strstr(makefile_content, "$(NIP05_TEST_EXEC): tests/nip05_test.c");
if (nip05_test_line) {
char* next_rule = strstr(nip05_test_line, "\n\n");
if (!next_rule) next_rule = makefile_content + strlen(makefile_content);
char* nip05_section = malloc(next_rule - nip05_test_line + 1);
strncpy(nip05_section, nip05_test_line, next_rule - nip05_test_line);
nip05_section[next_rule - nip05_test_line] = '\0';
ASSERT_CONTAINS(nip05_section, "./curl-install/lib/libcurl.a", "NIP-05 test uses static libcurl.a");
ASSERT_CONTAINS(nip05_section, "-static", "NIP-05 test uses static linking");
free(nip05_section);
}
}
// Test that only one Makefile exists
void test_single_makefile_policy() {
printf("\n=== Single Makefile Policy Test ===\n");
// Check that subdirectory Makefiles don't exist or are minimal/deprecated
int makefile_violations = 0;
// Check tests/Makefile
if (access("tests/Makefile", F_OK) == 0) {
char* tests_makefile = read_file("tests/Makefile");
if (tests_makefile) {
// If tests/Makefile exists and contains actual build rules, it's a violation
if (strstr(tests_makefile, "LDFLAGS") || strstr(tests_makefile, "gcc")) {
makefile_violations++;
printf("WARNING: tests/Makefile contains build rules (should be consolidated)\n");
}
free(tests_makefile);
}
}
// Check nostr_websocket/Makefile
if (access("nostr_websocket/Makefile", F_OK) == 0) {
char* websocket_makefile = read_file("nostr_websocket/Makefile");
if (websocket_makefile) {
// If websocket Makefile exists and contains build rules, it's a violation
if (strstr(websocket_makefile, "LDFLAGS") || strstr(websocket_makefile, "gcc")) {
makefile_violations++;
printf("WARNING: nostr_websocket/Makefile contains build rules (should be consolidated)\n");
}
free(websocket_makefile);
}
}
char message[256];
snprintf(message, sizeof(message), "No Makefile policy violations found (found %d violations)", makefile_violations);
ASSERT(makefile_violations == 0, message);
}
int main() {
printf("Makefile-Based Static Linking Test\n");
printf("==================================\n");
printf("Testing static linking configuration by parsing Makefile...\n");
// Read the main Makefile
char* makefile_content = read_file("Makefile");
if (!makefile_content) {
printf("FATAL: Cannot read Makefile\n");
return 1;
}
// Run all tests
test_static_linking_flags(makefile_content);
test_forbidden_dynamic_links(makefile_content);
test_static_library_usage(makefile_content);
test_compilation_flags(makefile_content);
test_static_curl_usage(makefile_content);
test_single_makefile_policy();
free(makefile_content);
// Summary
printf("\n============================================\n");
printf("TEST SUMMARY\n");
printf("============================================\n");
printf("Tests run: %d\n", tests_run);
printf("Tests passed: %d\n", tests_passed);
if (tests_passed == tests_run) {
printf("ALL TESTS PASSED!\n");
printf("✅ Makefile static linking configuration is correct\n");
printf("✅ No forbidden dynamic dependencies\n");
printf("✅ Single Makefile policy enforced\n");
return 0;
} else {
printf("%d TESTS FAILED!\n", tests_run - tests_passed);
printf("❌ Makefile configuration needs fixes\n");
return 1;
}
}

View File

@@ -1,816 +0,0 @@
/*
* NIP-04 Encryption Test with Known Test Vectors
* Uses test vectors from nostr-tools to validate our implementation
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../nostr_core/nostr_core.h"
void print_hex(const char* label, const unsigned char* data, size_t len) {
printf("%s: ", label);
for (size_t i = 0; i < len; i++) {
printf("%02x", data[i]);
}
printf("\n");
}
void hex_to_bytes(const char* hex_str, unsigned char* bytes) {
size_t len = strlen(hex_str);
for (size_t i = 0; i < len; i += 2) {
sscanf(hex_str + i, "%2hhx", &bytes[i / 2]);
}
}
// Simple replacement for strndup which isn't available in C99
char* safe_strndup(const char* s, size_t n) {
size_t len = strlen(s);
if (n < len) len = n;
char* result = malloc(len + 1);
if (result) {
strncpy(result, s, len);
result[len] = '\0';
}
return result;
}
int test_vector_1(void) {
printf("=== TEST VECTOR 1: Basic NIP-04 Encryption ===\n");
// Known test vector from nostr-tools
const char* sk1_hex = "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe";
const char* sk2_hex = "96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220";
const char* pk1_hex = "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1";
const char* pk2_hex = "dcb33a629560280a0ee3b6b99b68c044fe8914ad8a984001ebf6099a9b474dc3";
const char* plaintext = "nanana";
const char* expected_ciphertext = "zJxfaJ32rN5Dg1ODjOlEew==?iv=EV5bUjcc4OX2Km/zPp4ndQ==";
// Convert hex keys to bytes
unsigned char sk1[32], sk2[32], pk1[32], pk2[32];
hex_to_bytes(sk1_hex, sk1);
hex_to_bytes(sk2_hex, sk2);
hex_to_bytes(pk1_hex, pk1);
hex_to_bytes(pk2_hex, pk2);
printf("Input Test Vector:\n");
printf("SK1 (Alice): %s\n", sk1_hex);
printf("PK1 (Alice): %s\n", pk1_hex);
printf("SK2 (Bob): %s\n", sk2_hex);
printf("PK2 (Bob): %s\n", pk2_hex);
printf("Plaintext: \"%s\"\n", plaintext);
printf("Expected: %s\n", expected_ciphertext);
printf("\n");
// Test encryption (Alice -> Bob) - Use heap allocation to avoid stack overflow
char* encrypted = malloc(NOSTR_NIP04_MAX_ENCRYPTED_SIZE);
if (!encrypted) {
printf("❌ MEMORY ALLOCATION FAILED\n");
return 0;
}
printf("Testing encryption (Alice -> Bob)...\n");
int result = nostr_nip04_encrypt(sk1, pk2, plaintext, encrypted, NOSTR_NIP04_MAX_ENCRYPTED_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ ENCRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
printf("Our result: %s\n", encrypted);
printf("Expected: %s\n", expected_ciphertext);
// Note: Our encryption will have different IV, so ciphertext will differ
// The important test is that decryption works with both
printf("\n");
// Test decryption with our ciphertext (Bob decrypts message from Alice)
char* decrypted = malloc(NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (!decrypted) {
printf("❌ MEMORY ALLOCATION FAILED\n");
return 0;
}
printf("Testing decryption of our ciphertext (Bob decrypts from Alice)...\n");
result = nostr_nip04_decrypt(sk2, pk1, encrypted, decrypted, NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ DECRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
printf("Decrypted: \"%s\"\n", decrypted);
printf("Expected: \"%s\"\n", plaintext);
if (strcmp(plaintext, decrypted) == 0) {
printf("✅ Round-trip encryption/decryption: PASS\n");
} else {
printf("❌ Round-trip encryption/decryption: FAIL\n");
return 0;
}
// Test decryption with expected ciphertext (validation against reference implementation)
printf("\nTesting decryption of reference ciphertext...\n");
result = nostr_nip04_decrypt(sk2, pk1, expected_ciphertext, decrypted, NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ REFERENCE DECRYPTION FAILED: %s\n", nostr_strerror(result));
printf(" This suggests our implementation differs from reference\n");
return 0;
}
printf("Decrypted: \"%s\"\n", decrypted);
printf("Expected: \"%s\"\n", plaintext);
if (strcmp(plaintext, decrypted) == 0) {
printf("✅ Reference compatibility: PASS\n");
} else {
printf("❌ Reference compatibility: FAIL\n");
return 0;
}
printf("\n");
free(decrypted);
free(encrypted);
return 1;
}
int test_vector_2(void) {
printf("=== TEST VECTOR 2: Large Payload Test ===\n");
// Same keys as test vector 1
const char* sk1_hex = "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe";
const char* sk2_hex = "96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220";
const char* pk1_hex = "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1";
const char* pk2_hex = "dcb33a629560280a0ee3b6b99b68c044fe8914ad8a984001ebf6099a9b474dc3";
// Large payload: 800 'z' characters - allocate on heap to avoid stack overflow
char* large_plaintext = malloc(801);
if (!large_plaintext) {
printf("❌ MEMORY ALLOCATION FAILED\n");
return 0;
}
memset(large_plaintext, 'z', 800);
large_plaintext[800] = '\0';
const char* expected_ciphertext = "6f8dMstm+udOu7yipSn33orTmwQpWbtfuY95NH+eTU1kArysWJIDkYgI2D25EAGIDJsNd45jOJ2NbVOhFiL3ZP/NWsTwXokk34iyHyA/lkjzugQ1bHXoMD1fP/Ay4hB4al1NHb8HXHKZaxPrErwdRDb8qa/I6dXb/1xxyVvNQBHHvmsM5yIFaPwnCN1DZqXf2KbTA/Ekz7Hy+7R+Sy3TXLQDFpWYqykppkXc7Fs0qSuPRyxz5+anuN0dxZa9GTwTEnBrZPbthKkNRrvZMdTGJ6WumOh9aUq8OJJWy9aOgsXvs7qjN1UqcCqQqYaVnEOhCaqWNDsVtsFrVDj+SaLIBvCiomwF4C4nIgngJ5I69tx0UNI0q+ZnvOGQZ7m1PpW2NYP7Yw43HJNdeUEQAmdCPnh/PJwzLTnIxHmQU7n7SPlMdV0SFa6H8y2HHvex697GAkyE5t8c2uO24OnqIwF1tR3blIqXzTSRl0GA6QvrSj2p4UtnWjvF7xT7RiIEyTtgU/AsihTrXyXzWWZaIBJogpgw6erlZqWjCH7sZy/WoGYEiblobOAqMYxax6vRbeuGtoYksr/myX+x9rfLrYuoDRTw4woXOLmMrrj+Mf0TbAgc3SjdkqdsPU1553rlSqIEZXuFgoWmxvVQDtekgTYyS97G81TDSK9nTJT5ilku8NVq2LgtBXGwsNIw/xekcOUzJke3kpnFPutNaexR1VF3ohIuqRKYRGcd8ADJP2lfwMcaGRiplAmFoaVS1YUhQwYFNq9rMLf7YauRGV4BJg/t9srdGxf5RoKCvRo+XM/nLxxysTR9MVaEP/3lDqjwChMxs+eWfLHE5vRWV8hUEqdrWNZV29gsx5nQpzJ4PARGZVu310pQzc6JAlc2XAhhFk6RamkYJnmCSMnb/RblzIATBi2kNrCVAlaXIon188inB62rEpZGPkRIP7PUfu27S/elLQHBHeGDsxOXsBRo1gl3te+raoBHsxo6zvRnYbwdAQa5taDE63eh+fT6kFI+xYmXNAQkU8Dp0MVhEh4JQI06Ni/AKrvYpC95TXXIphZcF+/Pv/vaGkhG2X9S3uhugwWK?iv=2vWkOQQi0WynNJz/aZ4k2g==";
// Convert hex keys to bytes
unsigned char sk1[32], sk2[32], pk1[32], pk2[32];
hex_to_bytes(sk1_hex, sk1);
hex_to_bytes(sk2_hex, sk2);
hex_to_bytes(pk1_hex, pk1);
hex_to_bytes(pk2_hex, pk2);
printf("Input Test Vector:\n");
printf("SK1 (Alice): %s\n", sk1_hex);
printf("PK1 (Alice): %s\n", pk1_hex);
printf("SK2 (Bob): %s\n", sk2_hex);
printf("PK2 (Bob): %s\n", pk2_hex);
printf("Plaintext: 800 'z' characters\n");
char* truncated_expected = safe_strndup(expected_ciphertext, 80);
printf("Expected: %s...\n", truncated_expected);
free(truncated_expected);
printf("\n");
// Test encryption (Alice -> Bob) - Use heap allocation
char* encrypted = malloc(NOSTR_NIP04_MAX_ENCRYPTED_SIZE);
if (!encrypted) {
printf("❌ MEMORY ALLOCATION FAILED\n");
free(large_plaintext);
return 0;
}
printf("Testing encryption (Alice -> Bob)...\n");
int result = nostr_nip04_encrypt(sk1, pk2, large_plaintext, encrypted, NOSTR_NIP04_MAX_ENCRYPTED_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ ENCRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
char* truncated_result = safe_strndup(encrypted, 80);
printf("Our result: %s...\n", truncated_result);
free(truncated_result);
printf("Length: %zu bytes\n", strlen(encrypted));
printf("\n");
// Test decryption with our ciphertext
char* decrypted = malloc(NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (!decrypted) {
printf("❌ MEMORY ALLOCATION FAILED\n");
free(large_plaintext);
free(encrypted);
return 0;
}
printf("Testing decryption of our ciphertext...\n");
result = nostr_nip04_decrypt(sk2, pk1, encrypted, decrypted, NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ DECRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
printf("Decrypted length: %zu bytes\n", strlen(decrypted));
if (strcmp(large_plaintext, decrypted) == 0) {
printf("✅ Large payload round-trip: PASS\n");
} else {
printf("❌ Large payload round-trip: FAIL\n");
return 0;
}
// Test decryption with reference ciphertext
printf("\nTesting decryption of reference large payload...\n");
result = nostr_nip04_decrypt(sk2, pk1, expected_ciphertext, decrypted, NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ REFERENCE LARGE PAYLOAD DECRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
if (strcmp(large_plaintext, decrypted) == 0) {
printf("✅ Reference large payload compatibility: PASS\n");
} else {
printf("❌ Reference large payload compatibility: FAIL\n");
free(large_plaintext);
return 0;
}
printf("\n");
free(large_plaintext);
return 1;
}
int test_vector_3_bidirectional(void) {
printf("=== TEST VECTOR 3: Bidirectional Communication ===\n");
// Use the same keys but test both directions
const char* sk1_hex = "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe";
const char* sk2_hex = "96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220";
const char* pk1_hex = "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1";
const char* pk2_hex = "dcb33a629560280a0ee3b6b99b68c044fe8914ad8a984001ebf6099a9b474dc3";
const char* message_alice_to_bob = "Hello Bob, this is Alice!";
const char* message_bob_to_alice = "Hi Alice, Bob here. Message received!";
// Convert hex keys to bytes
unsigned char sk1[32], sk2[32], pk1[32], pk2[32];
hex_to_bytes(sk1_hex, sk1);
hex_to_bytes(sk2_hex, sk2);
hex_to_bytes(pk1_hex, pk1);
hex_to_bytes(pk2_hex, pk2);
printf("Input Test Vector:\n");
printf("SK1 (Alice): %s\n", sk1_hex);
printf("PK1 (Alice): %s\n", pk1_hex);
printf("SK2 (Bob): %s\n", sk2_hex);
printf("PK2 (Bob): %s\n", pk2_hex);
printf("Message A->B: \"%s\"\n", message_alice_to_bob);
printf("Message B->A: \"%s\"\n", message_bob_to_alice);
printf("\n");
// Test 1: Alice -> Bob - Use heap allocation
char* encrypted_a_to_b = malloc(NOSTR_NIP04_MAX_ENCRYPTED_SIZE);
char* decrypted_a_to_b = malloc(NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (!encrypted_a_to_b || !decrypted_a_to_b) {
printf("❌ MEMORY ALLOCATION FAILED\n");
free(encrypted_a_to_b);
free(decrypted_a_to_b);
return 0;
}
printf("Testing Alice -> Bob encryption...\n");
int result = nostr_nip04_encrypt(sk1, pk2, message_alice_to_bob, encrypted_a_to_b, NOSTR_NIP04_MAX_ENCRYPTED_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ A->B ENCRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
printf("Encrypted: %s\n", encrypted_a_to_b);
// Bob decrypts Alice's message
printf("Bob decrypting Alice's message...\n");
result = nostr_nip04_decrypt(sk2, pk1, encrypted_a_to_b, decrypted_a_to_b, NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ A->B DECRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
printf("Decrypted: \"%s\"\n", decrypted_a_to_b);
if (strcmp(message_alice_to_bob, decrypted_a_to_b) == 0) {
printf("✅ Alice -> Bob: PASS\n");
} else {
printf("❌ Alice -> Bob: FAIL\n");
return 0;
}
printf("\n");
// Test 2: Bob -> Alice - Use heap allocation
char* encrypted_b_to_a = malloc(NOSTR_NIP04_MAX_ENCRYPTED_SIZE);
char* decrypted_b_to_a = malloc(NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (!encrypted_b_to_a || !decrypted_b_to_a) {
printf("❌ MEMORY ALLOCATION FAILED\n");
free(encrypted_a_to_b);
free(decrypted_a_to_b);
free(encrypted_b_to_a);
free(decrypted_b_to_a);
return 0;
}
printf("Testing Bob -> Alice encryption...\n");
result = nostr_nip04_encrypt(sk2, pk1, message_bob_to_alice, encrypted_b_to_a, NOSTR_NIP04_MAX_ENCRYPTED_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ B->A ENCRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
printf("Encrypted: %s\n", encrypted_b_to_a);
// Alice decrypts Bob's message
printf("Alice decrypting Bob's message...\n");
result = nostr_nip04_decrypt(sk1, pk2, encrypted_b_to_a, decrypted_b_to_a, NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ B->A DECRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
printf("Decrypted: \"%s\"\n", decrypted_b_to_a);
if (strcmp(message_bob_to_alice, decrypted_b_to_a) == 0) {
printf("✅ Bob -> Alice: PASS\n");
} else {
printf("❌ Bob -> Alice: FAIL\n");
return 0;
}
printf("\n");
return 1;
}
int test_vector_4_random_keys(void) {
printf("=== TEST VECTOR 4: Random Keys - Hello, NOSTR! ===\n");
// Generated using nostr-tools with random keys
const char* sk1_hex = "5c5ea5ec3a804533ba8a21ba3dd981fc55a84e854dde53869b3f812ccd788200";
const char* pk1_hex = "0988b20763d3f8bc06e88722f2aa6b3caed3cc510e93287e1ee3f70ed22f54d2";
const char* sk2_hex = "8e94e91ea679509ec1f5da2be87352ea78acde2b69563c23a41b7f07c0891bc3";
const char* pk2_hex = "13747a8025c1196da3e67ecf941aa889c5c4ec6773e7f325f3f8d2435c4603c6";
const char* plaintext = "Hello, NOSTR!";
const char* expected_ciphertext = "+bqZAkfv/tI4h0XcvB9Baw==?iv=Om7m3at5zjJjxyAQbFY2IQ==";
// Convert hex keys to bytes
unsigned char sk1[32], sk2[32], pk1[32], pk2[32];
hex_to_bytes(sk1_hex, sk1);
hex_to_bytes(sk2_hex, sk2);
hex_to_bytes(pk1_hex, pk1);
hex_to_bytes(pk2_hex, pk2);
printf("Input Test Vector:\n");
printf("SK1 (Alice): %s\n", sk1_hex);
printf("PK1 (Alice): %s\n", pk1_hex);
printf("SK2 (Bob): %s\n", sk2_hex);
printf("PK2 (Bob): %s\n", pk2_hex);
printf("Plaintext: \"%s\"\n", plaintext);
printf("Expected: %s\n", expected_ciphertext);
printf("\n");
// Test encryption (Alice -> Bob) - Use heap allocation
char* encrypted = malloc(NOSTR_NIP04_MAX_ENCRYPTED_SIZE);
char* decrypted = malloc(NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (!encrypted || !decrypted) {
printf("❌ MEMORY ALLOCATION FAILED\n");
free(encrypted);
free(decrypted);
return 0;
}
printf("Testing encryption (Alice -> Bob)...\n");
int result = nostr_nip04_encrypt(sk1, pk2, plaintext, encrypted, NOSTR_NIP04_MAX_ENCRYPTED_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ ENCRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
printf("Our result: %s\n", encrypted);
// Test decryption with our ciphertext
printf("Testing decryption of our ciphertext...\n");
result = nostr_nip04_decrypt(sk2, pk1, encrypted, decrypted, NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ DECRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
printf("Decrypted: \"%s\"\n", decrypted);
printf("Expected: \"%s\"\n", plaintext);
if (strcmp(plaintext, decrypted) == 0) {
printf("✅ Round-trip encryption/decryption: PASS\n");
} else {
printf("❌ Round-trip encryption/decryption: FAIL\n");
return 0;
}
// Test decryption with reference ciphertext
printf("\nTesting decryption of reference ciphertext...\n");
result = nostr_nip04_decrypt(sk2, pk1, expected_ciphertext, decrypted, NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ REFERENCE DECRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
printf("Decrypted: \"%s\"\n", decrypted);
printf("Expected: \"%s\"\n", plaintext);
if (strcmp(plaintext, decrypted) == 0) {
printf("✅ Reference compatibility: PASS\n");
} else {
printf("❌ Reference compatibility: FAIL\n");
return 0;
}
printf("\n");
return 1;
}
int test_vector_5_long_message(void) {
printf("=== TEST VECTOR 5: Long Message with Emoji ===\n");
// Generated using nostr-tools with random keys
const char* sk1_hex = "51099e755aaab7e8ee1850b683b673c11d09799e85a630e951eb3c92fab4aed3";
const char* pk1_hex = "c5fb1cad7b11e3cf7f31d5bf47aaf3398a4803ea786eedfd674f55fa55dcb649";
const char* sk2_hex = "41f2788d00bd362ac3c7c784ee46e35b99765a086514ee69cb15de38c072309a";
const char* pk2_hex = "ba6773cf6a9b11476f692d4681a2f1e3015d1ee4a8d7c9d0364bed120f225079";
const char* plaintext = "This is a longer message to test encryption with more content. 🚀";
const char* expected_ciphertext = "3H9WEg9WjjN3r6ZymJt1R4ly3GlzhRR93FaSTGHLeM4oSS3eOnJtdXcO4ftgICMHRYM14WAmDDE9c12V8jhzua8GpnXKIVsNbY+oPF2yRwI=?iv=ztEGlo35pqJKrwZ2ZipsWg==";
// Convert hex keys to bytes
unsigned char sk1[32], sk2[32], pk1[32], pk2[32];
hex_to_bytes(sk1_hex, sk1);
hex_to_bytes(sk2_hex, sk2);
hex_to_bytes(pk1_hex, pk1);
hex_to_bytes(pk2_hex, pk2);
printf("Input Test Vector:\n");
printf("SK1 (Alice): %s\n", sk1_hex);
printf("PK1 (Alice): %s\n", pk1_hex);
printf("SK2 (Bob): %s\n", sk2_hex);
printf("PK2 (Bob): %s\n", pk2_hex);
printf("Plaintext: \"%s\"\n", plaintext);
printf("Expected: %s\n", expected_ciphertext);
printf("\n");
// Test encryption (Alice -> Bob) - Use heap allocation
char* encrypted = malloc(NOSTR_NIP04_MAX_ENCRYPTED_SIZE);
char* decrypted = malloc(NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (!encrypted || !decrypted) {
printf("❌ MEMORY ALLOCATION FAILED\n");
free(encrypted);
free(decrypted);
return 0;
}
printf("Testing encryption (Alice -> Bob)...\n");
int result = nostr_nip04_encrypt(sk1, pk2, plaintext, encrypted, NOSTR_NIP04_MAX_ENCRYPTED_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ ENCRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
printf("Our result: %s\n", encrypted);
// Test decryption with our ciphertext
printf("Testing decryption of our ciphertext...\n");
result = nostr_nip04_decrypt(sk2, pk1, encrypted, decrypted, NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ DECRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
printf("Decrypted: \"%s\"\n", decrypted);
printf("Expected: \"%s\"\n", plaintext);
if (strcmp(plaintext, decrypted) == 0) {
printf("✅ Round-trip encryption/decryption: PASS\n");
} else {
printf("❌ Round-trip encryption/decryption: FAIL\n");
return 0;
}
// Test decryption with reference ciphertext
printf("\nTesting decryption of reference ciphertext...\n");
result = nostr_nip04_decrypt(sk2, pk1, expected_ciphertext, decrypted, NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ REFERENCE DECRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
printf("Decrypted: \"%s\"\n", decrypted);
printf("Expected: \"%s\"\n", plaintext);
if (strcmp(plaintext, decrypted) == 0) {
printf("✅ Reference compatibility: PASS\n");
} else {
printf("❌ Reference compatibility: FAIL\n");
return 0;
}
printf("\n");
return 1;
}
int test_vector_6_short_message(void) {
printf("=== TEST VECTOR 6: Short Message ===\n");
// Generated using nostr-tools with random keys
const char* sk1_hex = "42c450eaebaee5ad94b602fc9054cde48f66d68c236b547aafee0ff319377290";
const char* pk1_hex = "a03f543eeb6c3f1c626181730751c39fd4f9f10455756d99ea855da97cf5076b";
const char* sk2_hex = "72f424c96239d271549c648d16635b5603ef32cdcbbff41058d14187b98f30cc";
const char* pk2_hex = "1c74b7a1d09ebeaf994a93a859682019930ad4f0f8ac7e65caacbbf4985042e8";
const char* plaintext = "Short";
const char* expected_ciphertext = "UIN92yHtAfX0vOTmn8VTtg==?iv=ou0QFU5UJUI6W4fUlkiElg==";
// Convert hex keys to bytes
unsigned char sk1[32], sk2[32], pk1[32], pk2[32];
hex_to_bytes(sk1_hex, sk1);
hex_to_bytes(sk2_hex, sk2);
hex_to_bytes(pk1_hex, pk1);
hex_to_bytes(pk2_hex, pk2);
printf("Input Test Vector:\n");
printf("SK1 (Alice): %s\n", sk1_hex);
printf("PK1 (Alice): %s\n", pk1_hex);
printf("SK2 (Bob): %s\n", sk2_hex);
printf("PK2 (Bob): %s\n", pk2_hex);
printf("Plaintext: \"%s\"\n", plaintext);
printf("Expected: %s\n", expected_ciphertext);
printf("\n");
// Test encryption (Alice -> Bob) - Use heap allocation
char* encrypted = malloc(NOSTR_NIP04_MAX_ENCRYPTED_SIZE);
char* decrypted = malloc(NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (!encrypted || !decrypted) {
printf("❌ MEMORY ALLOCATION FAILED\n");
free(encrypted);
free(decrypted);
return 0;
}
printf("Testing encryption (Alice -> Bob)...\n");
int result = nostr_nip04_encrypt(sk1, pk2, plaintext, encrypted, NOSTR_NIP04_MAX_ENCRYPTED_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ ENCRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
printf("Our result: %s\n", encrypted);
// Test decryption with our ciphertext
printf("Testing decryption of our ciphertext...\n");
result = nostr_nip04_decrypt(sk2, pk1, encrypted, decrypted, NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ DECRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
printf("Decrypted: \"%s\"\n", decrypted);
printf("Expected: \"%s\"\n", plaintext);
if (strcmp(plaintext, decrypted) == 0) {
printf("✅ Round-trip encryption/decryption: PASS\n");
} else {
printf("❌ Round-trip encryption/decryption: FAIL\n");
return 0;
}
// Test decryption with reference ciphertext
printf("\nTesting decryption of reference ciphertext...\n");
result = nostr_nip04_decrypt(sk2, pk1, expected_ciphertext, decrypted, NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ REFERENCE DECRYPTION FAILED: %s\n", nostr_strerror(result));
return 0;
}
printf("Decrypted: \"%s\"\n", decrypted);
printf("Expected: \"%s\"\n", plaintext);
if (strcmp(plaintext, decrypted) == 0) {
printf("✅ Reference compatibility: PASS\n");
} else {
printf("❌ Reference compatibility: FAIL\n");
return 0;
}
printf("\n");
return 1;
}
int test_vector_7_10kb_payload(void) {
printf("=== TEST VECTOR 7: 1MB Payload Stress Test ===\n");
// Same keys as previous tests for consistency
const char* sk1_hex = "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe";
const char* sk2_hex = "96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220";
const char* pk1_hex = "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1";
const char* pk2_hex = "dcb33a629560280a0ee3b6b99b68c044fe8914ad8a984001ebf6099a9b474dc3";
// Generate exactly 1MB (1,048,576 bytes) of predictable content
const size_t payload_size = 1048576;
char* large_plaintext = malloc(payload_size + 1);
if (!large_plaintext) {
printf("❌ MEMORY ALLOCATION FAILED for 1MB payload\n");
return 0;
}
// Fill with a predictable pattern: "ABCDEFGH01234567" repeated
const char* pattern = "ABCDEFGH01234567"; // 16 bytes
const size_t pattern_len = 16;
for (size_t i = 0; i < payload_size; i += pattern_len) {
size_t copy_len = (i + pattern_len <= payload_size) ? pattern_len : payload_size - i;
memcpy(large_plaintext + i, pattern, copy_len);
}
large_plaintext[payload_size] = '\0';
// Convert hex keys to bytes
unsigned char sk1[32], sk2[32], pk1[32], pk2[32];
hex_to_bytes(sk1_hex, sk1);
hex_to_bytes(sk2_hex, sk2);
hex_to_bytes(pk1_hex, pk1);
hex_to_bytes(pk2_hex, pk2);
printf("Input Test Vector:\n");
printf("SK1 (Alice): %s\n", sk1_hex);
printf("PK1 (Alice): %s\n", pk1_hex);
printf("SK2 (Bob): %s\n", sk2_hex);
printf("PK2 (Bob): %s\n", pk2_hex);
printf("Plaintext: 1,048,576 bytes (exactly 1MB) of pattern data\n");
printf("Pattern: \"%s\" repeated\n", pattern);
printf("First 64 chars: \"%.64s...\"\n", large_plaintext);
printf("Last 64 chars: \"...%.64s\"\n", large_plaintext + payload_size - 64);
printf("\n");
// Test encryption (Alice -> Bob) - Use heap allocation
char* encrypted = malloc(NOSTR_NIP04_MAX_ENCRYPTED_SIZE);
if (!encrypted) {
printf("❌ MEMORY ALLOCATION FAILED for encrypted buffer\n");
free(large_plaintext);
return 0;
}
printf("Testing encryption (Alice -> Bob) with 1MB payload...\n");
printf("Expected padded size: %zu bytes (1MB + PKCS#7 padding)\n", ((payload_size / 16) + 1) * 16);
int result = nostr_nip04_encrypt(sk1, pk2, large_plaintext, encrypted, NOSTR_NIP04_MAX_ENCRYPTED_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ 1MB ENCRYPTION FAILED: %s\n", nostr_strerror(result));
free(large_plaintext);
free(encrypted);
return 0;
}
size_t encrypted_len = strlen(encrypted);
printf("✅ 1MB encryption SUCCESS!\n");
printf("Encrypted length: %zu bytes\n", encrypted_len);
printf("First 80 chars: \"%.80s...\"\n", encrypted);
printf("Last 80 chars: \"...%.80s\"\n", encrypted + encrypted_len - 80);
printf("\n");
// Test decryption with our ciphertext
char* decrypted = malloc(NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (!decrypted) {
printf("❌ MEMORY ALLOCATION FAILED for decrypted buffer\n");
free(large_plaintext);
free(encrypted);
return 0;
}
printf("Testing decryption of 1MB ciphertext (Bob decrypts from Alice)...\n");
result = nostr_nip04_decrypt(sk2, pk1, encrypted, decrypted, NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
if (result != NOSTR_SUCCESS) {
printf("❌ 1MB DECRYPTION FAILED: %s\n", nostr_strerror(result));
free(large_plaintext);
free(encrypted);
free(decrypted);
return 0;
}
size_t decrypted_len = strlen(decrypted);
printf("✅ 1MB decryption SUCCESS!\n");
printf("Decrypted length: %zu bytes\n", decrypted_len);
// Verify length matches
if (decrypted_len != payload_size) {
printf("❌ LENGTH MISMATCH: Expected %zu bytes, got %zu bytes\n", payload_size, decrypted_len);
free(large_plaintext);
free(encrypted);
free(decrypted);
return 0;
}
// Verify content matches exactly
if (memcmp(large_plaintext, decrypted, payload_size) == 0) {
printf("✅ 1MB payload round-trip: PASS\n");
printf("✅ Content verification: All %zu bytes match perfectly!\n", payload_size);
} else {
printf("❌ 1MB payload round-trip: FAIL - Content mismatch detected\n");
// Find first mismatch for debugging
for (size_t i = 0; i < payload_size; i++) {
if (large_plaintext[i] != decrypted[i]) {
printf("First mismatch at byte %zu: expected 0x%02x, got 0x%02x\n",
i, (unsigned char)large_plaintext[i], (unsigned char)decrypted[i]);
break;
}
}
free(large_plaintext);
free(encrypted);
free(decrypted);
return 0;
}
printf("\n🎉 1MB STRESS TEST COMPLETED SUCCESSFULLY! 🎉\n");
printf("Memory management: All allocations and frees successful\n");
printf("Buffer safety: No heap corruption detected\n");
printf("PKCS#7 padding: Correctly handled for large payload\n");
printf("Base64 encoding: Successfully processed large ciphertext\n");
printf("Performance: 1MB encrypt/decrypt cycle completed\n");
printf("\n");
free(large_plaintext);
free(encrypted);
free(decrypted);
return 1;
}
int main(void) {
printf("=== NIP-04 Encryption Test with Reference Test Vectors ===\n\n");
// Initialize the library
if (nostr_init() != NOSTR_SUCCESS) {
printf("ERROR: Failed to initialize NOSTR library\n");
return 1;
}
int all_passed = 1;
// Run all test vectors
if (!test_vector_1()) {
all_passed = 0;
}
if (!test_vector_2()) {
all_passed = 0;
}
if (!test_vector_3_bidirectional()) {
all_passed = 0;
}
if (!test_vector_4_random_keys()) {
all_passed = 0;
}
if (!test_vector_5_long_message()) {
all_passed = 0;
}
if (!test_vector_6_short_message()) {
all_passed = 0;
}
if (!test_vector_7_10kb_payload()) {
all_passed = 0;
}
// Summary
printf("=== TEST SUMMARY ===\n");
if (all_passed) {
printf("🎉 ALL TESTS PASSED! NIP-04 implementation is working correctly.\n");
printf("\n");
printf("Our library successfully:\n");
printf("- Encrypts and decrypts small messages\n");
printf("- Handles large payloads (800+ characters)\n");
printf("- Supports bidirectional communication\n");
printf("- Works with random key pairs from nostr-tools\n");
printf("- Handles messages with Unicode emoji characters\n");
printf("- Processes both short and long messages correctly\n");
printf("- Is 100%% compatible with reference implementations\n");
printf("\n");
printf("Total test vectors: 6 (including 3 generated with nostr-tools)\n");
} else {
printf("❌ SOME TESTS FAILED. Please review the output above.\n");
}
nostr_cleanup();
return all_passed ? 0 : 1;
}

View File

@@ -1,239 +0,0 @@
/*
* NIP-11 Relay Information Document Test
*
* Test suite for NIP-11 relay information document fetching and parsing.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../nostr_core/nostr_core.h"
#include "../cjson/cJSON.h"
// Test counter
static int tests_run = 0;
static int tests_passed = 0;
#define TEST_ASSERT(condition, message) do { \
tests_run++; \
if (condition) { \
printf("✅ %s\n", message); \
tests_passed++; \
} else { \
printf("❌ %s\n", message); \
} \
} while(0)
/**
* Test basic NIP-11 functionality by fetching relay information from popular relays
*/
void test_nip11_fetch_relay_info(void) {
printf("\n=== NIP-11 Relay Information Tests ===\n");
// Test popular relays
const char* test_relays[] = {
"wss://relay.damus.io",
"wss://nos.lol",
"wss://relay.nostr.band"
};
int relay_count = sizeof(test_relays) / sizeof(test_relays[0]);
for (int i = 0; i < relay_count; i++) {
printf("\n[TEST] Fetching relay info for %s\n", test_relays[i]);
nostr_relay_info_t* info = NULL;
int result = nostr_nip11_fetch_relay_info(test_relays[i], &info, 10);
if (result == NOSTR_SUCCESS && info) {
printf("✅ Successfully fetched relay information\n");
// Display basic information
if (info->basic.name) {
printf(" 📛 Name: %s\n", info->basic.name);
}
if (info->basic.description) {
printf(" 📝 Description: %.100s%s\n",
info->basic.description,
strlen(info->basic.description) > 100 ? "..." : "");
}
if (info->basic.software) {
printf(" 💻 Software: %s\n", info->basic.software);
}
if (info->basic.version) {
printf(" 🏷️ Version: %s\n", info->basic.version);
}
if (info->basic.pubkey) {
printf(" 🔑 Admin pubkey: %.16s...\n", info->basic.pubkey);
}
if (info->basic.contact) {
printf(" 📧 Contact: %s\n", info->basic.contact);
}
// Display supported NIPs
if (info->basic.supported_nips && info->basic.supported_nips_count > 0) {
printf(" 🛠️ Supported NIPs (%zu): ", info->basic.supported_nips_count);
for (size_t j = 0; j < info->basic.supported_nips_count; j++) {
printf("%d", info->basic.supported_nips[j]);
if (j < info->basic.supported_nips_count - 1) printf(", ");
}
printf("\n");
}
// Display limitations if present
if (info->has_limitations) {
printf(" ⚠️ Server Limitations:\n");
if (info->limitations.max_message_length > 0) {
printf(" Max message length: %d bytes\n", info->limitations.max_message_length);
}
if (info->limitations.max_subscriptions > 0) {
printf(" Max subscriptions: %d\n", info->limitations.max_subscriptions);
}
if (info->limitations.auth_required >= 0) {
printf(" Auth required: %s\n", info->limitations.auth_required ? "Yes" : "No");
}
if (info->limitations.payment_required >= 0) {
printf(" Payment required: %s\n", info->limitations.payment_required ? "Yes" : "No");
}
}
// Display content limitations
if (info->has_content_limitations && info->content_limitations.relay_countries_count > 0) {
printf(" 🌍 Relay countries: ");
for (size_t j = 0; j < info->content_limitations.relay_countries_count; j++) {
printf("%s", info->content_limitations.relay_countries[j]);
if (j < info->content_limitations.relay_countries_count - 1) printf(", ");
}
printf("\n");
}
// Display community preferences
if (info->has_community_preferences) {
if (info->community_preferences.language_tags_count > 0) {
printf(" 🗣️ Languages: ");
for (size_t j = 0; j < info->community_preferences.language_tags_count; j++) {
printf("%s", info->community_preferences.language_tags[j]);
if (j < info->community_preferences.language_tags_count - 1) printf(", ");
}
printf("\n");
}
if (info->community_preferences.tags_count > 0) {
printf(" 🏷️ Community tags: ");
for (size_t j = 0; j < info->community_preferences.tags_count; j++) {
printf("%s", info->community_preferences.tags[j]);
if (j < info->community_preferences.tags_count - 1) printf(", ");
}
printf("\n");
}
if (info->community_preferences.posting_policy) {
printf(" 📋 Posting policy: %s\n", info->community_preferences.posting_policy);
}
}
// Display icon
if (info->has_icon && info->icon.icon) {
printf(" 🎨 Icon: %s\n", info->icon.icon);
}
// Verify we got at least some basic information
TEST_ASSERT(info->basic.name || info->basic.description || info->basic.software,
"Relay provided basic information");
nostr_nip11_relay_info_free(info);
} else {
printf("⚠️ Failed to fetch relay information: %s\n", nostr_strerror(result));
printf(" (This might be expected for some relays that don't support NIP-11)\n");
}
}
}
/**
* Test NIP-11 JSON parsing with known good data
*/
void test_nip11_json_parsing(void) {
printf("\n=== JSON Parsing Tests ===\n");
// This is testing internal functionality - we'll create a simple test
// by using a mock HTTP response
printf("[TEST] JSON parsing with minimal data\n");
// For now, we can only test the full fetch workflow since parsing is internal
// A more complete test would expose the parsing function or use dependency injection
printf("✅ JSON parsing test deferred to integration testing\n");
tests_run++;
tests_passed++;
}
/**
* Test URL conversion functionality
*/
void test_url_conversion(void) {
printf("\n=== URL Conversion Tests ===\n");
// We test this indirectly by trying different URL formats
// The conversion happens internally in the NIP-11 implementation
printf("[TEST] URL conversion handled internally\n");
printf("✅ Different URL formats are handled by the implementation\n");
tests_run++;
tests_passed++;
}
/**
* Test error handling
*/
void test_error_handling(void) {
printf("\n=== Error Handling Tests ===\n");
// Test with invalid parameters
nostr_relay_info_t* info = NULL;
int result = nostr_nip11_fetch_relay_info(NULL, &info, 10);
TEST_ASSERT(result == NOSTR_ERROR_INVALID_INPUT, "NULL URL rejected");
result = nostr_nip11_fetch_relay_info("wss://relay.example.com", NULL, 10);
TEST_ASSERT(result == NOSTR_ERROR_INVALID_INPUT, "NULL output pointer rejected");
// Test with non-existent relay
result = nostr_nip11_fetch_relay_info("wss://non-existent-relay-12345.invalid", &info, 2);
TEST_ASSERT(result != NOSTR_SUCCESS, "Non-existent relay fails appropriately");
// Test free function with NULL (should not crash)
nostr_nip11_relay_info_free(NULL);
printf("✅ Free function handles NULL safely\n");
tests_run++;
tests_passed++;
}
int main(void) {
printf("NOSTR NIP-11 Test Suite\n");
printf("=======================\n");
// Initialize the library
if (nostr_init() != NOSTR_SUCCESS) {
printf("❌ Failed to initialize NOSTR library\n");
return 1;
}
// Run tests
test_nip11_fetch_relay_info();
test_nip11_json_parsing();
test_url_conversion();
test_error_handling();
// Summary
printf("\n=== Summary ===\n");
printf("Tests run: %d\n", tests_run);
printf("Tests passed: %d\n", tests_passed);
printf("Tests failed: %d\n", tests_run - tests_passed);
if (tests_passed == tests_run) {
printf("✅ All tests passed!\n");
} else {
printf("❌ Some tests failed.\n");
}
// Cleanup
nostr_cleanup();
return (tests_passed == tests_run) ? 0 : 1;
}

View File

@@ -8,7 +8,7 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "../nostr_core/nostr_crypto.h"
#include "../nostr_core/utils.h"
// Helper function to convert hex string to bytes
static void hex_to_bytes(const char* hex, unsigned char* bytes, size_t len) {