/* * NIP-01 Event Validation Test Suite * Tests event structure validation and cryptographic verification * Following TESTS POLICY: Shows expected vs actual values, prints entire JSON events */ #define _GNU_SOURCE // For strdup on Linux #include #include #include #include #include "../nostr_core/nip001.h" #include "../nostr_core/nostr_common.h" #include "../nostr_core/utils.h" #include "../cjson/cJSON.h" // Ensure strdup is declared #ifndef strdup extern char *strdup(const char *s); #endif // Test counter for tracking progress static int test_count = 0; static int passed_tests = 0; void print_test_header(const char* test_name) { test_count++; printf("\n=== TEST %d: %s ===\n", test_count, test_name); } void print_test_result(int passed, const char* test_name) { if (passed) { passed_tests++; printf("✅ PASS: %s\n", test_name); } else { printf("❌ FAIL: %s\n", test_name); } } void print_json_comparison(const char* label, cJSON* expected, cJSON* actual) { char* expected_str = cJSON_Print(expected); char* actual_str; if (actual) { actual_str = cJSON_Print(actual); } else { actual_str = strdup("NULL"); } printf("%s Expected JSON:\n%s\n", label, expected_str ? expected_str : "NULL"); printf("%s Actual JSON:\n%s\n", label, actual_str ? actual_str : "NULL"); if (expected_str) free(expected_str); if (actual_str) free(actual_str); } // Test vector 1: Valid event from nak (should pass all validation) int test_valid_event_1(void) { print_test_header("Valid Event from nak - Basic"); // Generated with: nak event --sec nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99 -c "Test event 1" -k 1 const char* event_json = "{" "\"kind\":1," "\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\"," "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\"," "\"created_at\":1755599338," "\"tags\":[]," "\"content\":\"Test event 1\"," "\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\"" "}"; printf("Input Event JSON:\n%s\n\n", event_json); cJSON* event = cJSON_Parse(event_json); if (!event) { printf("❌ JSON Parse Error\n"); return 0; } printf("Testing Structure Validation...\n"); int structure_result = nostr_validate_event_structure(event); printf("Expected: NOSTR_SUCCESS (0)\n"); printf("Actual: %d (%s)\n", structure_result, nostr_strerror(structure_result)); if (structure_result != NOSTR_SUCCESS) { printf("❌ Structure validation failed\n"); cJSON_Delete(event); return 0; } printf("✅ Structure validation passed\n\n"); printf("Testing Cryptographic Verification...\n"); int crypto_result = nostr_verify_event_signature(event); printf("Expected: NOSTR_SUCCESS (0)\n"); printf("Actual: %d (%s)\n", crypto_result, nostr_strerror(crypto_result)); if (crypto_result != NOSTR_SUCCESS) { printf("❌ Cryptographic verification failed\n"); cJSON_Delete(event); return 0; } printf("✅ Cryptographic verification passed\n\n"); printf("Testing Complete Validation...\n"); int complete_result = nostr_validate_event(event); printf("Expected: NOSTR_SUCCESS (0)\n"); printf("Actual: %d (%s)\n", complete_result, nostr_strerror(complete_result)); cJSON_Delete(event); return (complete_result == NOSTR_SUCCESS); } // Test vector 2: Valid event with tags (should pass all validation) int test_valid_event_with_tags(void) { print_test_header("Valid Event with Tags"); // Generated with: nak event --sec nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99 -c "Test event with tags" -k 1 -t "hello=world" -t "test=value" const char* event_json = "{" "\"kind\":1," "\"id\":\"781bf3185479315350c4039f719c3a8859a1ebb21a4b04e0e649addf0ae4665b\"," "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\"," "\"created_at\":1755599345," "\"tags\":[[\"hello\",\"world\"],[\"test\",\"value\"]]," "\"content\":\"Test event with tags\"," "\"sig\":\"3a390ece9d746bd576cb8fbba6f9ed59730099781d0d414d1194f832ca072f23e7764935451a025399202e02f28a59fde2d4c25399e241f3de2ad0b3a4830d6a\"" "}"; printf("Input Event JSON:\n%s\n\n", event_json); cJSON* event = cJSON_Parse(event_json); if (!event) { printf("❌ JSON Parse Error\n"); return 0; } int result = nostr_validate_event(event); printf("Expected: NOSTR_SUCCESS (0)\n"); printf("Actual: %d (%s)\n", result, nostr_strerror(result)); cJSON_Delete(event); return (result == NOSTR_SUCCESS); } // Test vector 3: Valid metadata event (kind 0) int test_valid_metadata_event(void) { print_test_header("Valid Metadata Event (Kind 0)"); // Generated with: nak event --sec nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99 -c "" -k 0 --ts 1640995200 const char* event_json = "{" "\"kind\":0," "\"id\":\"a2ed5175224f6597bc3a553e91d6fcea2e13f199e56afc90ccecca1c890b308a\"," "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\"," "\"created_at\":1640995200," "\"tags\":[]," "\"content\":\"\"," "\"sig\":\"b9b2859ded73bbc010331145e2d297b30205d2b94d80ef0619077b687d53756b8d9eb3954ef1483b814059cd879c992153ca9d7b50dcf7053a3d530cd82fb884\"" "}"; printf("Input Event JSON:\n%s\n\n", event_json); cJSON* event = cJSON_Parse(event_json); if (!event) { printf("❌ JSON Parse Error\n"); return 0; } int result = nostr_validate_event(event); printf("Expected: NOSTR_SUCCESS (0)\n"); printf("Actual: %d (%s)\n", result, nostr_strerror(result)); cJSON_Delete(event); return (result == NOSTR_SUCCESS); } // Test 4: Missing required field (id) int test_missing_id_field(void) { print_test_header("Missing Required Field - ID"); const char* event_json = "{" "\"kind\":1," "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\"," "\"created_at\":1755599338," "\"tags\":[]," "\"content\":\"Test event 1\"," "\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\"" "}"; printf("Input Event JSON (missing 'id' field):\n%s\n\n", event_json); cJSON* event = cJSON_Parse(event_json); if (!event) { printf("❌ JSON Parse Error\n"); return 0; } int result = nostr_validate_event_structure(event); printf("Expected: NOSTR_ERROR_EVENT_INVALID_STRUCTURE (-30)\n"); printf("Actual: %d (%s)\n", result, nostr_strerror(result)); cJSON_Delete(event); return (result == NOSTR_ERROR_EVENT_INVALID_STRUCTURE); } // Test 5: Invalid hex string length (pubkey too short) int test_invalid_pubkey_length(void) { print_test_header("Invalid Pubkey Length"); const char* event_json = "{" "\"kind\":1," "\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\"," "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7\"," // Too short (63 chars instead of 64) "\"created_at\":1755599338," "\"tags\":[]," "\"content\":\"Test event 1\"," "\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\"" "}"; printf("Input Event JSON (pubkey too short - 63 chars instead of 64):\n%s\n\n", event_json); cJSON* event = cJSON_Parse(event_json); if (!event) { printf("❌ JSON Parse Error\n"); return 0; } int result = nostr_validate_event_structure(event); printf("Expected: NOSTR_ERROR_EVENT_INVALID_PUBKEY (-32)\n"); printf("Actual: %d (%s)\n", result, nostr_strerror(result)); cJSON_Delete(event); return (result == NOSTR_ERROR_EVENT_INVALID_PUBKEY); } // Test 6: Invalid kind (negative) int test_invalid_kind_negative(void) { print_test_header("Invalid Kind - Negative Value"); const char* event_json = "{" "\"kind\":-1," // Invalid negative kind "\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\"," "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\"," "\"created_at\":1755599338," "\"tags\":[]," "\"content\":\"Test event 1\"," "\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\"" "}"; printf("Input Event JSON (kind = -1):\n%s\n\n", event_json); cJSON* event = cJSON_Parse(event_json); if (!event) { printf("❌ JSON Parse Error\n"); return 0; } int result = nostr_validate_event_structure(event); printf("Expected: NOSTR_ERROR_EVENT_INVALID_KIND (-35)\n"); printf("Actual: %d (%s)\n", result, nostr_strerror(result)); cJSON_Delete(event); return (result == NOSTR_ERROR_EVENT_INVALID_KIND); } // Test 7: Invalid tags (not an array) int test_invalid_tags_not_array(void) { print_test_header("Invalid Tags - Not Array"); const char* event_json = "{" "\"kind\":1," "\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\"," "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\"," "\"created_at\":1755599338," "\"tags\":\"not an array\"," // Should be array "\"content\":\"Test event 1\"," "\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\"" "}"; printf("Input Event JSON (tags is string instead of array):\n%s\n\n", event_json); cJSON* event = cJSON_Parse(event_json); if (!event) { printf("❌ JSON Parse Error\n"); return 0; } int result = nostr_validate_event_structure(event); printf("Expected: NOSTR_ERROR_EVENT_INVALID_TAGS (-36)\n"); printf("Actual: %d (%s)\n", result, nostr_strerror(result)); cJSON_Delete(event); return (result == NOSTR_ERROR_EVENT_INVALID_TAGS); } // Test 8: Wrong event ID (cryptographic test) int test_wrong_event_id(void) { print_test_header("Wrong Event ID - Cryptographic Test"); // Take valid event but change the ID to something incorrect const char* event_json = "{" "\"kind\":1," "\"id\":\"0000000000000000000000000000000000000000000000000000000000000000\"," // Wrong ID "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\"," "\"created_at\":1755599338," "\"tags\":[]," "\"content\":\"Test event 1\"," "\"sig\":\"b9c7148e5a3e4ae1985a4b83a6317df72e2ef78dc4389063b7b83d5642aeccb5fb42f7e820ccfaf38c8077f9f6fef1fa1fd7d05c27c7fbc797cf53ee249ea640\"" "}"; printf("Input Event JSON (ID changed to all zeros):\n%s\n\n", event_json); cJSON* event = cJSON_Parse(event_json); if (!event) { printf("❌ JSON Parse Error\n"); return 0; } // Structure should pass int structure_result = nostr_validate_event_structure(event); printf("Structure validation - Expected: NOSTR_SUCCESS (0)\n"); printf("Structure validation - Actual: %d (%s)\n", structure_result, nostr_strerror(structure_result)); if (structure_result != NOSTR_SUCCESS) { printf("❌ Unexpected structure validation failure\n"); cJSON_Delete(event); return 0; } // Crypto should fail int crypto_result = nostr_verify_event_signature(event); printf("Crypto verification - Expected: NOSTR_ERROR_EVENT_INVALID_ID (-31)\n"); printf("Crypto verification - Actual: %d (%s)\n", crypto_result, nostr_strerror(crypto_result)); cJSON_Delete(event); return (crypto_result == NOSTR_ERROR_EVENT_INVALID_ID); } // Test 9: Invalid signature (cryptographic test) int test_invalid_signature(void) { print_test_header("Invalid Signature - Cryptographic Test"); // Take valid event but change the signature to a structurally valid but cryptographically wrong signature const char* event_json = "{" "\"kind\":1," "\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\"," "\"pubkey\":\"aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4\"," "\"created_at\":1755599338," "\"tags\":[]," "\"content\":\"Test event 1\"," "\"sig\":\"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef\"" // Wrong but valid hex "}"; printf("Input Event JSON (signature changed to invalid hex):\n%s\n\n", event_json); cJSON* event = cJSON_Parse(event_json); if (!event) { printf("❌ JSON Parse Error\n"); return 0; } // Structure should pass int structure_result = nostr_validate_event_structure(event); printf("Structure validation - Expected: NOSTR_SUCCESS (0)\n"); printf("Structure validation - Actual: %d (%s)\n", structure_result, nostr_strerror(structure_result)); if (structure_result != NOSTR_SUCCESS) { printf("❌ Unexpected structure validation failure\n"); cJSON_Delete(event); return 0; } // Crypto should fail int crypto_result = nostr_verify_event_signature(event); printf("Crypto verification - Expected: NOSTR_ERROR_EVENT_INVALID_SIGNATURE (-33)\n"); printf("Crypto verification - Actual: %d (%s)\n", crypto_result, nostr_strerror(crypto_result)); cJSON_Delete(event); return (crypto_result == NOSTR_ERROR_EVENT_INVALID_SIGNATURE); } // Test 10: Integration test with our own event creation int test_integration_with_event_creation(void) { print_test_header("Integration Test - Validate Our Own Created Event"); // Create a test event using our existing function const char* private_key_hex = "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe"; unsigned char private_key[32]; nostr_hex_to_bytes(private_key_hex, private_key, 32); cJSON* tags = cJSON_CreateArray(); cJSON* tag = cJSON_CreateArray(); cJSON_AddItemToArray(tag, cJSON_CreateString("test")); cJSON_AddItemToArray(tag, cJSON_CreateString("integration")); cJSON_AddItemToArray(tags, tag); printf("Creating event with our library...\n"); cJSON* created_event = nostr_create_and_sign_event(1, "Integration test message", tags, private_key, time(NULL)); if (!created_event) { printf("❌ Event creation failed\n"); cJSON_Delete(tags); return 0; } char* event_str = cJSON_Print(created_event); printf("Created Event JSON:\n%s\n\n", event_str); free(event_str); printf("Validating our created event...\n"); int result = nostr_validate_event(created_event); printf("Expected: NOSTR_SUCCESS (0)\n"); printf("Actual: %d (%s)\n", result, nostr_strerror(result)); if (result == NOSTR_SUCCESS) { printf("✅ Our library creates valid events that pass validation\n"); } else { printf("❌ Our own created event failed validation\n"); } cJSON_Delete(created_event); return (result == NOSTR_SUCCESS); } int main(void) { printf("=== NIP-01 Event Validation Test Suite ===\n"); printf("Following TESTS POLICY: Shows expected vs actual values, prints entire JSON events\n"); // Initialize crypto library if (nostr_init() != NOSTR_SUCCESS) { printf("❌ Failed to initialize nostr library\n"); return 1; } int all_passed = 1; int test_result; // Valid event tests test_result = test_valid_event_1(); print_test_result(test_result, "Valid Event from nak - Basic"); if (!test_result) all_passed = 0; test_result = test_valid_event_with_tags(); print_test_result(test_result, "Valid Event with Tags"); if (!test_result) all_passed = 0; test_result = test_valid_metadata_event(); print_test_result(test_result, "Valid Metadata Event (Kind 0)"); if (!test_result) all_passed = 0; // Invalid structure tests test_result = test_missing_id_field(); print_test_result(test_result, "Missing Required Field - ID"); if (!test_result) all_passed = 0; test_result = test_invalid_pubkey_length(); print_test_result(test_result, "Invalid Pubkey Length"); if (!test_result) all_passed = 0; test_result = test_invalid_kind_negative(); print_test_result(test_result, "Invalid Kind - Negative Value"); if (!test_result) all_passed = 0; test_result = test_invalid_tags_not_array(); print_test_result(test_result, "Invalid Tags - Not Array"); if (!test_result) all_passed = 0; // Invalid cryptography tests test_result = test_wrong_event_id(); print_test_result(test_result, "Wrong Event ID - Cryptographic Test"); if (!test_result) all_passed = 0; test_result = test_invalid_signature(); print_test_result(test_result, "Invalid Signature - Cryptographic Test"); if (!test_result) all_passed = 0; // Integration test test_result = test_integration_with_event_creation(); print_test_result(test_result, "Integration Test - Validate Our Own Created Event"); if (!test_result) all_passed = 0; // Summary printf("\n=== TEST SUMMARY ===\n"); printf("Total tests: %d\n", test_count); printf("Passed: %d\n", passed_tests); printf("Failed: %d\n", test_count - passed_tests); if (all_passed) { printf("🎉 ALL TESTS PASSED! Event validation implementation is working correctly.\n"); } else { printf("❌ SOME TESTS FAILED. Please review the output above.\n"); } nostr_cleanup(); return all_passed ? 0 : 1; }