Fully statically linked for both x64 and arm64. Updated build.sh to always compile both versions

This commit is contained in:
2025-08-11 06:54:50 -04:00
parent ae4aa7cf80
commit d257ae49f1
12 changed files with 1526 additions and 59 deletions

View File

@@ -16,6 +16,7 @@ RELAY_POOL_TEST_EXEC = relay_pool_test
EVENT_GEN_TEST_EXEC = test_event_generation
POW_LOOP_TEST_EXEC = test_pow_loop
NIP04_TEST_EXEC = nip04_test
STATIC_LINKING_TEST_EXEC = static_linking_only_test
ARM64_CRYPTO_TEST_EXEC = nostr_crypto_test_arm64
ARM64_CORE_TEST_EXEC = nostr_core_test_arm64
ARM64_RELAY_POOL_TEST_EXEC = relay_pool_test_arm64
@@ -54,6 +55,11 @@ $(NIP04_TEST_EXEC): nip04_test.c
@echo "Building NIP-04 encryption test suite (x86_64)..."
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
# Build static linking test executable (x86_64)
$(STATIC_LINKING_TEST_EXEC): static_linking_only_test.c
@echo "Building static linking verification test (x86_64)..."
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
# Build simple initialization test executable (x86_64)
simple_init_test: simple_init_test.c
@echo "Building simple initialization test program (x86_64)..."
@@ -142,8 +148,13 @@ test-nip04: $(NIP04_TEST_EXEC)
@echo "Running NIP-04 encryption tests (x86_64)..."
./$(NIP04_TEST_EXEC)
# Run static linking verification test (x86_64)
test-static-linking: $(STATIC_LINKING_TEST_EXEC)
@echo "Running static linking verification test (x86_64)..."
./$(STATIC_LINKING_TEST_EXEC)
# Run all test suites (x86_64)
test: test-crypto test-core test-relay-pool test-nip04
test: test-crypto test-core test-relay-pool test-nip04 test-static-linking
# Run crypto tests ARM64 (requires qemu-user-static or ARM64 system)
test-crypto-arm64: $(ARM64_CRYPTO_TEST_EXEC)
@@ -190,7 +201,7 @@ test-all: test test-arm64
# Clean
clean:
@echo "Cleaning test artifacts..."
rm -f $(CRYPTO_TEST_EXEC) $(CORE_TEST_EXEC) $(RELAY_POOL_TEST_EXEC) $(EVENT_GEN_TEST_EXEC) $(POW_LOOP_TEST_EXEC) $(NIP04_TEST_EXEC) $(ARM64_CRYPTO_TEST_EXEC) $(ARM64_CORE_TEST_EXEC) $(ARM64_RELAY_POOL_TEST_EXEC) $(ARM64_NIP04_TEST_EXEC)
rm -f $(CRYPTO_TEST_EXEC) $(CORE_TEST_EXEC) $(RELAY_POOL_TEST_EXEC) $(EVENT_GEN_TEST_EXEC) $(POW_LOOP_TEST_EXEC) $(NIP04_TEST_EXEC) $(STATIC_LINKING_TEST_EXEC) $(ARM64_CRYPTO_TEST_EXEC) $(ARM64_CORE_TEST_EXEC) $(ARM64_RELAY_POOL_TEST_EXEC) $(ARM64_NIP04_TEST_EXEC)
# Help
help:
@@ -198,17 +209,18 @@ help:
@echo "================"
@echo ""
@echo "Available targets:"
@echo " all - Build all test executables (x86_64)"
@echo " all-arch - Build test executables for both x86_64 and ARM64"
@echo " test-crypto - Build and run crypto tests (x86_64)"
@echo " test-core - Build and run core tests (x86_64)"
@echo " test-relay-pool - Build and run relay pool tests (x86_64)"
@echo " test-nip04 - Build and run NIP-04 encryption tests (x86_64)"
@echo " test - Build and run all test suites (x86_64)"
@echo " test-arm64 - Build and run all test suites (ARM64)"
@echo " test-all - Run tests on both architectures"
@echo " clean - Remove test artifacts"
@echo " help - Show this help"
@echo " all - Build all test executables (x86_64)"
@echo " all-arch - Build test executables for both x86_64 and ARM64"
@echo " test-crypto - Build and run crypto tests (x86_64)"
@echo " test-core - Build and run core tests (x86_64)"
@echo " test-relay-pool - Build and run relay pool tests (x86_64)"
@echo " test-nip04 - Build and run NIP-04 encryption tests (x86_64)"
@echo " test-static-linking - Build and run static linking verification test (x86_64)"
@echo " test - Build and run all test suites (x86_64)"
@echo " test-arm64 - Build and run all test suites (ARM64)"
@echo " test-all - Run tests on both architectures"
@echo " clean - Remove test artifacts"
@echo " help - Show this help"
@echo ""
@echo "Test Executables:"
@echo " $(CRYPTO_TEST_EXEC) - x86_64 crypto test binary"
@@ -221,9 +233,10 @@ help:
@echo " $(ARM64_NIP04_TEST_EXEC) - ARM64 NIP-04 encryption test binary"
@echo ""
@echo "Test Coverage:"
@echo " Crypto Tests - Low-level cryptographic primitives (SHA-256, HMAC, secp256k1, BIP39, BIP32)"
@echo " Core Tests - High-level NOSTR functionality with nak compatibility validation"
@echo " Relay Pool Tests - Relay pool event processing with real NOSTR relays"
@echo " NIP-04 Tests - NOSTR NIP-04 encryption/decryption with reference test vectors"
@echo " Crypto Tests - Low-level cryptographic primitives (SHA-256, HMAC, secp256k1, BIP39, BIP32)"
@echo " Core Tests - High-level NOSTR functionality with nak compatibility validation"
@echo " Relay Pool Tests - Relay pool event processing with real NOSTR relays"
@echo " NIP-04 Tests - NOSTR NIP-04 encryption/decryption with reference test vectors"
@echo " Static Linking Tests - Verify library has no external crypto dependencies (self-contained)"
.PHONY: all all-arch test-crypto test-core test-relay-pool test test-crypto-arm64 test-core-arm64 test-relay-pool-arm64 test-arm64 test-all clean help
.PHONY: all all-arch test-crypto test-core test-relay-pool test-nip04 test-static-linking test test-crypto-arm64 test-core-arm64 test-relay-pool-arm64 test-arm64 test-all clean help

BIN
tests/static_linking_only_test Executable file

Binary file not shown.

View File

@@ -0,0 +1,416 @@
/*
* NOSTR Core Library - Static Linking Only Test
*
* This test verifies that the library maintains its self-contained,
* static-only design with no external cryptographic dependencies.
*
* Test Categories:
* 1. Library dependency analysis using ldd/otool
* 2. Symbol resolution verification using nm/objdump
* 3. Build process validation
* 4. Runtime independence verification
* 5. Library size and content verification
*/
#define _GNU_SOURCE // For popen/pclose on Linux
#include "../nostr_core/nostr_core.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include "../cjson/cJSON.h"
// ANSI color codes for output
#define GREEN "\033[32m"
#define RED "\033[31m"
#define YELLOW "\033[33m"
#define BLUE "\033[34m"
#define RESET "\033[0m"
// Test result tracking
static int tests_run = 0;
static int tests_passed = 0;
// Helper function to run shell commands and capture output
static int run_command(const char* command, char* output, size_t output_size) {
FILE* fp = popen(command, "r");
if (!fp) {
return -1;
}
size_t total = 0;
while (total < output_size - 1 && fgets(output + total, output_size - total, fp)) {
total = strlen(output);
}
int status = pclose(fp);
return WEXITSTATUS(status);
}
// Helper function to check if file exists
static int file_exists(const char* path) {
struct stat st;
return stat(path, &st) == 0;
}
// Test macro
#define RUN_TEST(test_name, test_func) do { \
printf(BLUE "[TEST] " RESET "%s...\n", test_name); \
tests_run++; \
if (test_func()) { \
printf(GREEN "[PASS] " RESET "%s\n\n", test_name); \
tests_passed++; \
} else { \
printf(RED "[FAIL] " RESET "%s\n\n", test_name); \
} \
} while(0)
// Test 1: Library Dependency Analysis
static int test_library_dependency_analysis(void) {
char command[512];
char output[4096];
int result;
// Check if we have the main library
if (!file_exists("../libnostr_core.a")) {
printf(RED "ERROR: " RESET "libnostr_core.a not found. Run 'make' first.\n");
return 0;
}
// Create a simple test binary to analyze
printf("Creating test binary for dependency analysis...\n");
const char* test_code =
"#include \"../nostr_core/nostr_core.h\"\n"
"#include <stdio.h>\n"
"int main() {\n"
" if (nostr_init() == NOSTR_SUCCESS) {\n"
" unsigned char privkey[32], pubkey[32];\n"
" if (nostr_generate_keypair(privkey, pubkey) == NOSTR_SUCCESS) {\n"
" printf(\"Crypto test passed\\n\");\n"
" }\n"
" nostr_cleanup();\n"
" }\n"
" return 0;\n"
"}\n";
FILE* fp = fopen("/tmp/static_test.c", "w");
if (!fp) {
printf(RED "ERROR: " RESET "Cannot create temporary test file\n");
return 0;
}
fputs(test_code, fp);
fclose(fp);
// Compile the test binary
snprintf(command, sizeof(command),
"gcc -I.. -Wall -Wextra -std=c99 /tmp/static_test.c -o /tmp/static_test ../libnostr_core.a -lm -static 2>/dev/null");
result = system(command);
if (result != 0) {
printf(RED "ERROR: " RESET "Failed to compile test binary\n");
return 0;
}
// Analyze dependencies with ldd (Linux) or otool (macOS)
printf("Analyzing binary dependencies...\n");
#ifdef __linux__
snprintf(command, sizeof(command), "ldd /tmp/static_test 2>&1");
#elif __APPLE__
snprintf(command, sizeof(command), "otool -L /tmp/static_test 2>&1");
#else
printf(YELLOW "WARNING: " RESET "Unknown platform, skipping dependency analysis\n");
cleanup_and_return:
unlink("/tmp/static_test.c");
unlink("/tmp/static_test");
return 1;
#endif
result = run_command(command, output, sizeof(output));
// Check for problematic dynamic dependencies
const char* forbidden_libs[] = {
"libsecp256k1",
"libssl",
"libcrypto",
"libwally",
"libsodium"
};
int found_forbidden = 0;
for (int i = 0; i < 5; i++) {
if (strstr(output, forbidden_libs[i])) {
printf(RED "ERROR: " RESET "Found forbidden dynamic dependency: %s\n", forbidden_libs[i]);
found_forbidden = 1;
}
}
if (!found_forbidden) {
printf(GREEN "GOOD: " RESET "No forbidden cryptographic dependencies found\n");
}
// For static binaries, ldd should say "not a dynamic executable" or show minimal deps
#ifdef __linux__
if (strstr(output, "not a dynamic executable") || strstr(output, "statically linked")) {
printf(GREEN "EXCELLENT: " RESET "Binary is statically linked\n");
} else {
printf(YELLOW "INFO: " RESET "Binary appears to have some dynamic dependencies:\n");
printf("%s\n", output);
}
#endif
// Cleanup
unlink("/tmp/static_test.c");
unlink("/tmp/static_test");
return !found_forbidden;
}
// Test 2: Symbol Resolution Verification
static int test_symbol_resolution_verification(void) {
char command[512];
char output[8192];
printf("Verifying secp256k1 symbols are present in static library...\n");
// Check that critical secp256k1 symbols are present
snprintf(command, sizeof(command), "nm ../libnostr_core.a 2>/dev/null | grep secp256k1");
if (run_command(command, output, sizeof(output)) != 0 || strlen(output) == 0) {
printf(RED "ERROR: " RESET "No secp256k1 symbols found in library\n");
return 0;
}
// Check for key secp256k1 functions
const char* required_symbols[] = {
"secp256k1_context_create",
"secp256k1_ec_pubkey_create",
"secp256k1_schnorrsig_sign",
"secp256k1_schnorrsig_verify",
"secp256k1_ecdh"
};
int symbols_found = 0;
for (int i = 0; i < 5; i++) {
if (strstr(output, required_symbols[i])) {
symbols_found++;
printf(GREEN "FOUND: " RESET "%s\n", required_symbols[i]);
} else {
printf(YELLOW "MISSING: " RESET "%s\n", required_symbols[i]);
}
}
if (symbols_found >= 3) {
printf(GREEN "GOOD: " RESET "Found %d/5 critical secp256k1 symbols\n", symbols_found);
return 1;
} else {
printf(RED "ERROR: " RESET "Only found %d/5 critical secp256k1 symbols\n", symbols_found);
return 0;
}
}
// Test 3: Build Process Validation
static int test_build_process_validation(void) {
char command[512];
int result;
printf("Testing minimal build requirements...\n");
// Test that we can build with only libnostr_core.a and -lm
const char* minimal_test =
"#include \"../nostr_core/nostr_core.h\"\n"
"int main() { return nostr_init() == NOSTR_SUCCESS ? 0 : 1; }\n";
FILE* fp = fopen("/tmp/minimal_test.c", "w");
if (!fp) return 0;
fputs(minimal_test, fp);
fclose(fp);
// Try to build with minimal dependencies
snprintf(command, sizeof(command),
"gcc -I.. -Wall -Wextra -std=c99 /tmp/minimal_test.c -o /tmp/minimal_test ../libnostr_core.a -lm 2>/dev/null");
result = system(command);
unlink("/tmp/minimal_test.c");
if (result == 0) {
printf(GREEN "EXCELLENT: " RESET "Can build with only libnostr_core.a and -lm\n");
// Test that it actually runs
result = system("/tmp/minimal_test");
unlink("/tmp/minimal_test");
if (result == 0) {
printf(GREEN "EXCELLENT: " RESET "Minimal binary runs successfully\n");
return 1;
} else {
printf(RED "ERROR: " RESET "Minimal binary failed to run\n");
return 0;
}
} else {
printf(RED "ERROR: " RESET "Cannot build with minimal dependencies\n");
unlink("/tmp/minimal_test");
return 0;
}
}
// Test 4: Runtime Independence Test
static int test_runtime_independence(void) {
printf("Testing runtime independence (crypto functionality)...\n");
// Initialize the library
if (nostr_init() != NOSTR_SUCCESS) {
printf(RED "ERROR: " RESET "Library initialization failed\n");
return 0;
}
// Test key generation
unsigned char private_key[32];
unsigned char public_key[32];
if (nostr_generate_keypair(private_key, public_key) != NOSTR_SUCCESS) {
printf(RED "ERROR: " RESET "Key generation failed\n");
nostr_cleanup();
return 0;
}
printf(GREEN "GOOD: " RESET "Key generation works\n");
// Test bech32 encoding
char nsec[100], npub[100];
if (nostr_key_to_bech32(private_key, "nsec", nsec) != NOSTR_SUCCESS ||
nostr_key_to_bech32(public_key, "npub", npub) != NOSTR_SUCCESS) {
printf(RED "ERROR: " RESET "Bech32 encoding failed\n");
nostr_cleanup();
return 0;
}
printf(GREEN "GOOD: " RESET "Bech32 encoding works\n");
// Test signing
cJSON* event = nostr_create_and_sign_event(1, "Test message", NULL, private_key, 0);
if (!event) {
printf(RED "ERROR: " RESET "Event creation/signing failed\n");
nostr_cleanup();
return 0;
}
printf(GREEN "GOOD: " RESET "Event signing works\n");
cJSON_Delete(event);
// Test NIP-44 encryption if available
char plaintext[] = "Hello, NOSTR!";
char encrypted[1024];
char decrypted[1024];
// Generate recipient keys
unsigned char recipient_private[32], recipient_public[32];
nostr_generate_keypair(recipient_private, recipient_public);
if (nostr_nip44_encrypt(private_key, recipient_public, plaintext, encrypted, sizeof(encrypted)) == NOSTR_SUCCESS) {
if (nostr_nip44_decrypt(recipient_private, public_key, encrypted, decrypted, sizeof(decrypted)) == NOSTR_SUCCESS) {
if (strcmp(plaintext, decrypted) == 0) {
printf(GREEN "EXCELLENT: " RESET "NIP-44 encryption/decryption works\n");
} else {
printf(YELLOW "WARNING: " RESET "NIP-44 decryption mismatch\n");
}
} else {
printf(YELLOW "WARNING: " RESET "NIP-44 decryption failed\n");
}
} else {
printf(YELLOW "WARNING: " RESET "NIP-44 encryption failed (may not be enabled)\n");
}
nostr_cleanup();
return 1;
}
// Test 5: Library Size and Content Verification
static int test_library_size_and_content(void) {
struct stat st;
char command[512];
char output[4096];
printf("Verifying library size and content...\n");
// Check library size
if (stat("../libnostr_core.a", &st) != 0) {
printf(RED "ERROR: " RESET "Cannot stat libnostr_core.a\n");
return 0;
}
size_t lib_size = st.st_size;
printf("Library size: %zu bytes (%.2f MB)\n", lib_size, lib_size / 1024.0 / 1024.0);
// Expect "fat" library to be at least 1MB (with secp256k1 bundled)
if (lib_size < 1024 * 1024) {
printf(YELLOW "WARNING: " RESET "Library seems small (%.2f MB). May not include secp256k1.\n",
lib_size / 1024.0 / 1024.0);
} else {
printf(GREEN "GOOD: " RESET "Library size suggests secp256k1 is bundled\n");
}
// List archive contents
snprintf(command, sizeof(command), "ar -t ../libnostr_core.a | wc -l");
if (run_command(command, output, sizeof(output)) == 0) {
int object_count = atoi(output);
printf("Archive contains %d object files\n", object_count);
if (object_count > 20) {
printf(GREEN "EXCELLENT: " RESET "High object count suggests secp256k1 objects included\n");
} else {
printf(YELLOW "WARNING: " RESET "Low object count (%d). secp256k1 may not be fully bundled\n", object_count);
}
}
// Check for secp256k1-specific object files
snprintf(command, sizeof(command), "ar -t ../libnostr_core.a | grep -E '(secp256k1|ecmult)' | head -5");
if (run_command(command, output, sizeof(output)) == 0 && strlen(output) > 0) {
printf(GREEN "EXCELLENT: " RESET "Found secp256k1 object files in archive:\n");
printf("%s", output);
} else {
printf(YELLOW "WARNING: " RESET "No obvious secp256k1 object files found\n");
}
return 1;
}
// Main test runner
int main(int argc, char* argv[]) {
(void)argc;
(void)argv;
printf(BLUE "NOSTR Core Library - Static Linking Only Test\n");
printf("==============================================" RESET "\n\n");
printf("This test verifies that the library maintains its self-contained,\n");
printf("static-only design with no external cryptographic dependencies.\n\n");
// Run all tests
RUN_TEST("Library Dependency Analysis", test_library_dependency_analysis);
RUN_TEST("Symbol Resolution Verification", test_symbol_resolution_verification);
RUN_TEST("Build Process Validation", test_build_process_validation);
RUN_TEST("Runtime Independence Test", test_runtime_independence);
RUN_TEST("Library Size and Content Verification", test_library_size_and_content);
// Print summary
printf(BLUE "============================================\n");
printf("TEST SUMMARY\n");
printf("============================================" RESET "\n");
printf("Tests run: %d\n", tests_run);
printf("Tests passed: %d\n", tests_passed);
if (tests_passed == tests_run) {
printf(GREEN "ALL TESTS PASSED!" RESET "\n");
printf("✅ Library maintains static-only design\n");
printf("✅ No external crypto dependencies\n");
printf("✅ Self-contained and portable\n");
} else {
printf(RED "SOME TESTS FAILED!" RESET "\n");
printf("❌ %d out of %d tests failed\n", tests_run - tests_passed, tests_run);
printf("⚠️ Library may have external dependencies or missing components\n");
}
return (tests_passed == tests_run) ? 0 : 1;
}