/* * NIP-13 Proof of Work Test Suite * Tests PoW generation, difficulty calculation, nonce extraction, and validation * Following TESTS POLICY: Shows expected vs actual values, prints entire JSON events */ #define _GNU_SOURCE // For strdup on Linux #include #include #include #include #include #include "../nostr_core/nip013.h" #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); } } // Test 1: Difficulty calculation with NIP-13 example int test_difficulty_calculation_reference(void) { print_test_header("Difficulty Calculation - NIP-13 Reference"); // Event ID from NIP-13 spec: 000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358 // This should have ~20 leading zero bits as stated in the spec const char* event_id = "000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358"; printf("Testing event ID: %s\n", event_id); printf("Expected: ~20 leading zero bits\n"); int difficulty = nostr_calculate_pow_difficulty(event_id); printf("Actual: %d leading zero bits\n", difficulty); if (difficulty < 0) { printf("❌ Error calculating difficulty: %d (%s)\n", difficulty, nostr_strerror(difficulty)); return 0; } // The NIP-13 spec example should have around 20 bits if (difficulty >= 19 && difficulty <= 21) { printf("✅ Difficulty calculation matches expected range (19-21 bits)\n"); return 1; } else { printf("❌ Difficulty %d is outside expected range (19-21 bits)\n", difficulty); return 0; } } // Test 2: Extract nonce info from NIP-13 reference event int test_nonce_extraction_reference(void) { print_test_header("Nonce Extraction - NIP-13 Reference Event"); // Complete event from NIP-13 spec const char* event_json = "{" "\"id\":\"000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358\"," "\"pubkey\":\"a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243\"," "\"created_at\":1651794653," "\"kind\":1," "\"tags\":[[\"nonce\",\"776797\",\"20\"]]," "\"content\":\"It's just me mining my own business\"," "\"sig\":\"284622fc0a3f4f1303455d5175f7ba962a3300d136085b9566801bc2e0699de0c7e31e44c81fb40ad9049173742e904713c3594a1da0fc5d2382a25c11aba977\"" "}"; 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; } uint64_t nonce_value = 0; int target_difficulty = -1; int result = nostr_extract_nonce_info(event, &nonce_value, &target_difficulty); printf("Extraction result: %d (%s)\n", result, nostr_strerror(result)); printf("Expected nonce: 776797\n"); printf("Actual nonce: %llu\n", (unsigned long long)nonce_value); printf("Expected target: 20\n"); printf("Actual target: %d\n", target_difficulty); cJSON_Delete(event); if (result != NOSTR_SUCCESS) { printf("❌ Failed to extract nonce info\n"); return 0; } if (nonce_value == 776797 && target_difficulty == 20) { printf("✅ Nonce extraction successful\n"); return 1; } else { printf("❌ Extracted values don't match expected\n"); return 0; } } // Test 3: PoW validation with reference event int test_pow_validation_reference(void) { print_test_header("PoW Validation - NIP-13 Reference Event"); const char* event_json = "{" "\"id\":\"000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358\"," "\"pubkey\":\"a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243\"," "\"created_at\":1651794653," "\"kind\":1," "\"tags\":[[\"nonce\",\"776797\",\"20\"]]," "\"content\":\"It's just me mining my own business\"," "\"sig\":\"284622fc0a3f4f1303455d5175f7ba962a3300d136085b9566801bc2e0699de0c7e31e44c81fb40ad9049173742e904713c3594a1da0fc5d2382a25c11aba977\"" "}"; 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; } // Test basic validation (no minimum difficulty) nostr_pow_result_t result_info; int validation_result = nostr_validate_pow(event, 0, NOSTR_POW_VALIDATE_BASIC, &result_info); printf("Validation result: %d (%s)\n", validation_result, nostr_strerror(validation_result)); printf("Actual difficulty: %d\n", result_info.actual_difficulty); printf("Committed target: %d\n", result_info.committed_target); printf("Nonce value: %llu\n", (unsigned long long)result_info.nonce_value); printf("Has nonce tag: %d\n", result_info.has_nonce_tag); printf("Error detail: %s\n", result_info.error_detail); cJSON_Delete(event); return (validation_result == NOSTR_SUCCESS && result_info.has_nonce_tag == 1); } // Test 4: Generate PoW with our library and validate it int test_pow_generation_and_validation(void) { print_test_header("PoW Generation and Validation - Our Library"); // Create a test event for mining 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(); printf("Creating base event...\n"); cJSON* event = nostr_create_and_sign_event(1, "Testing PoW generation", tags, private_key, time(NULL)); if (!event) { printf("❌ Event creation failed\n"); cJSON_Delete(tags); return 0; } char* original_event_str = cJSON_Print(event); printf("Original event (no PoW):\n%s\n\n", original_event_str); free(original_event_str); // Add PoW with difficulty 8 (reasonable for testing) printf("Adding PoW with difficulty 8...\n"); int pow_result = nostr_add_proof_of_work(event, private_key, 8, 100000, 10000, 10000, NULL, NULL); if (pow_result != NOSTR_SUCCESS) { printf("❌ PoW generation failed: %d (%s)\n", pow_result, nostr_strerror(pow_result)); cJSON_Delete(event); return 0; } char* pow_event_str = cJSON_Print(event); printf("Event with PoW:\n%s\n\n", pow_event_str); free(pow_event_str); // Now validate the PoW printf("Validating generated PoW...\n"); nostr_pow_result_t result_info; int validation_result = nostr_validate_pow(event, 8, NOSTR_POW_VALIDATE_FULL, &result_info); printf("Validation result: %d (%s)\n", validation_result, nostr_strerror(validation_result)); printf("Actual difficulty: %d\n", result_info.actual_difficulty); printf("Committed target: %d\n", result_info.committed_target); printf("Has nonce tag: %d\n", result_info.has_nonce_tag); printf("Error detail: %s\n", result_info.error_detail); cJSON_Delete(event); if (validation_result == NOSTR_SUCCESS && result_info.actual_difficulty >= 8) { printf("✅ PoW generation and validation successful\n"); return 1; } else { printf("❌ PoW validation failed\n"); return 0; } } // Test 5: Test various validation flag combinations int test_validation_flags(void) { print_test_header("Validation Flags - Various Combinations"); // Use NIP-13 reference event const char* event_json = "{" "\"id\":\"000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358\"," "\"pubkey\":\"a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243\"," "\"created_at\":1651794653," "\"kind\":1," "\"tags\":[[\"nonce\",\"776797\",\"20\"]]," "\"content\":\"It's just me mining my own business\"," "\"sig\":\"284622fc0a3f4f1303455d5175f7ba962a3300d136085b9566801bc2e0699de0c7e31e44c81fb40ad9049173742e904713c3594a1da0fc5d2382a25c11aba977\"" "}"; cJSON* event = cJSON_Parse(event_json); if (!event) { printf("❌ JSON Parse Error\n"); return 0; } int all_passed = 1; nostr_pow_result_t result_info; // Test 1: Basic validation printf("\nSubtest 1: NOSTR_POW_VALIDATE_BASIC\n"); int result = nostr_validate_pow(event, 0, NOSTR_POW_VALIDATE_BASIC, &result_info); printf("Result: %d (%s)\n", result, nostr_strerror(result)); if (result != NOSTR_SUCCESS) all_passed = 0; // Test 2: Full validation printf("\nSubtest 2: NOSTR_POW_VALIDATE_FULL\n"); result = nostr_validate_pow(event, 0, NOSTR_POW_VALIDATE_FULL, &result_info); printf("Result: %d (%s)\n", result, nostr_strerror(result)); if (result != NOSTR_SUCCESS) all_passed = 0; // Test 3: Anti-spam validation (should pass since committed target matches actual) printf("\nSubtest 3: NOSTR_POW_VALIDATE_ANTI_SPAM\n"); result = nostr_validate_pow(event, 0, NOSTR_POW_VALIDATE_ANTI_SPAM, &result_info); printf("Result: %d (%s)\n", result, nostr_strerror(result)); if (result != NOSTR_SUCCESS) all_passed = 0; // Test 4: Minimum difficulty requirement (15 bits - should pass) printf("\nSubtest 4: Minimum difficulty 15 bits\n"); result = nostr_validate_pow(event, 15, NOSTR_POW_VALIDATE_BASIC, &result_info); printf("Result: %d (%s)\n", result, nostr_strerror(result)); if (result != NOSTR_SUCCESS) all_passed = 0; // Test 5: Too high minimum difficulty (30 bits - should fail) printf("\nSubtest 5: Minimum difficulty 30 bits (should fail)\n"); result = nostr_validate_pow(event, 30, NOSTR_POW_VALIDATE_BASIC, &result_info); printf("Result: %d (%s)\n", result, nostr_strerror(result)); if (result == NOSTR_SUCCESS) { printf("❌ Should have failed but didn't\n"); all_passed = 0; } else if (result == NOSTR_ERROR_NIP13_INSUFFICIENT) { printf("✅ Correctly failed with insufficient difficulty\n"); } else { printf("❌ Failed with unexpected error\n"); all_passed = 0; } cJSON_Delete(event); return all_passed; } // Test 6: Error conditions and edge cases int test_error_conditions(void) { print_test_header("Error Conditions and Edge Cases"); int all_passed = 1; // Test 1: NULL event printf("\nSubtest 1: NULL event\n"); int result = nostr_validate_pow(NULL, 0, NOSTR_POW_VALIDATE_BASIC, NULL); printf("Expected: NOSTR_ERROR_INVALID_INPUT (-1)\n"); printf("Actual: %d (%s)\n", result, nostr_strerror(result)); if (result != NOSTR_ERROR_INVALID_INPUT) all_passed = 0; // Test 2: Event without ID printf("\nSubtest 2: Event without ID\n"); const char* no_id_json = "{\"kind\":1,\"content\":\"test\",\"tags\":[]}"; cJSON* no_id_event = cJSON_Parse(no_id_json); result = nostr_validate_pow(no_id_event, 0, NOSTR_POW_VALIDATE_BASIC, NULL); printf("Expected: NOSTR_ERROR_EVENT_INVALID_ID (-31)\n"); printf("Actual: %d (%s)\n", result, nostr_strerror(result)); if (result != NOSTR_ERROR_EVENT_INVALID_ID) all_passed = 0; cJSON_Delete(no_id_event); // Test 3: Event without nonce tag when required printf("\nSubtest 3: Event without nonce tag when required\n"); const char* no_nonce_json = "{" "\"id\":\"f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffaf0\"," "\"kind\":1,\"content\":\"test\",\"tags\":[]" "}"; cJSON* no_nonce_event = cJSON_Parse(no_nonce_json); result = nostr_validate_pow(no_nonce_event, 0, NOSTR_POW_VALIDATE_NONCE_TAG, NULL); printf("Expected: NOSTR_ERROR_NIP13_NO_NONCE_TAG (-101)\n"); printf("Actual: %d (%s)\n", result, nostr_strerror(result)); if (result != NOSTR_ERROR_NIP13_NO_NONCE_TAG) all_passed = 0; cJSON_Delete(no_nonce_event); // Test 4: Invalid hex ID printf("\nSubtest 4: Invalid hex ID\n"); result = nostr_calculate_pow_difficulty("invalid_hex_string"); printf("Expected: NOSTR_ERROR_NIP13_CALCULATION (-104)\n"); printf("Actual: %d (%s)\n", result, nostr_strerror(result)); if (result != NOSTR_ERROR_NIP13_CALCULATION) all_passed = 0; // Test 5: Wrong length hex ID printf("\nSubtest 5: Wrong length hex ID\n"); result = nostr_calculate_pow_difficulty("f1e582c90f071c0110cc5bcac2dcc6d8c32250e3cc26fcbe93470d918f2ffa"); // 63 chars instead of 64 printf("Expected: NOSTR_ERROR_NIP13_CALCULATION (-104)\n"); printf("Actual: %d (%s)\n", result, nostr_strerror(result)); if (result != NOSTR_ERROR_NIP13_CALCULATION) all_passed = 0; return all_passed; } // Test 7: Integration with nak-generated PoW events int test_nak_integration(void) { print_test_header("Integration with nak-generated PoW events"); // Generate a test event with nak and PoW difficulty 8 printf("Generating PoW event with nak (difficulty 8)...\n"); // Create a temporary key for this test char temp_key[] = "/tmp/nip13_test_key_XXXXXX"; int fd = mkstemp(temp_key); if (fd == -1) { printf("❌ Failed to create temporary key file\n"); return 0; } // Write test key to file (same as used in other tests) const char* test_key = "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe"; ssize_t bytes_written = write(fd, test_key, strlen(test_key)); close(fd); if (bytes_written != (ssize_t)strlen(test_key)) { printf("❌ Failed to write test key to temporary file\n"); unlink(temp_key); return 0; } // Generate event with PoW using nak char cmd[1024]; snprintf(cmd, sizeof(cmd), "nak event --sec %s -c \"PoW test from nak\" --pow 8 --ts %ld", test_key, (long)time(NULL)); printf("Executing: %s\n", cmd); FILE* pipe = popen(cmd, "r"); if (!pipe) { printf("❌ Failed to execute nak command\n"); unlink(temp_key); return 0; } char event_json[4096] = {0}; if (!fgets(event_json, sizeof(event_json), pipe)) { printf("❌ Failed to read nak output\n"); pclose(pipe); unlink(temp_key); return 0; } pclose(pipe); unlink(temp_key); // Remove trailing newline event_json[strcspn(event_json, "\n")] = 0; printf("nak-generated event:\n%s\n\n", event_json); // Parse and validate the event cJSON* event = cJSON_Parse(event_json); if (!event) { printf("❌ Failed to parse nak-generated JSON\n"); return 0; } // Validate the PoW nostr_pow_result_t result_info; int validation_result = nostr_validate_pow(event, 8, NOSTR_POW_VALIDATE_FULL, &result_info); printf("Validation result: %d (%s)\n", validation_result, nostr_strerror(validation_result)); printf("Actual difficulty: %d\n", result_info.actual_difficulty); printf("Committed target: %d\n", result_info.committed_target); printf("Has nonce tag: %d\n", result_info.has_nonce_tag); printf("Error detail: %s\n", result_info.error_detail); cJSON_Delete(event); if (validation_result == NOSTR_SUCCESS && result_info.actual_difficulty >= 8) { printf("✅ nak-generated PoW event validates successfully\n"); return 1; } else { printf("❌ nak-generated PoW event validation failed\n"); return 0; } } int main(void) { printf("=== NIP-13 Proof of Work Test Suite ===\n"); printf("Following TESTS POLICY: Shows expected vs actual values, prints entire JSON events\n"); printf("Tests both PoW generation and validation functionality\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; // Test 1: Basic difficulty calculation test_result = test_difficulty_calculation_reference(); print_test_result(test_result, "Difficulty Calculation - NIP-13 Reference"); if (!test_result) all_passed = 0; // Test 2: Nonce extraction test_result = test_nonce_extraction_reference(); print_test_result(test_result, "Nonce Extraction - NIP-13 Reference Event"); if (!test_result) all_passed = 0; // Test 3: Basic PoW validation test_result = test_pow_validation_reference(); print_test_result(test_result, "PoW Validation - NIP-13 Reference Event"); if (!test_result) all_passed = 0; // Test 4: PoW generation and validation test_result = test_pow_generation_and_validation(); print_test_result(test_result, "PoW Generation and Validation - Our Library"); if (!test_result) all_passed = 0; // Test 5: Validation flags test_result = test_validation_flags(); print_test_result(test_result, "Validation Flags - Various Combinations"); if (!test_result) all_passed = 0; // Test 6: Error conditions test_result = test_error_conditions(); print_test_result(test_result, "Error Conditions and Edge Cases"); if (!test_result) all_passed = 0; // Test 7: nak integration (optional - may fail if nak not available) printf("\n=== Optional nak Integration Test ===\n"); test_result = test_nak_integration(); if (test_result) { print_test_result(test_result, "Integration with nak-generated PoW events"); } else { printf("⚠️ SKIP: Integration with nak-generated PoW events (nak may not be available)\n"); // Don't fail the entire test suite for this optional test } // 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! NIP-13 PoW implementation is working correctly.\n"); printf("✅ PoW generation works\n"); printf("✅ PoW difficulty calculation works\n"); printf("✅ Nonce extraction works\n"); printf("✅ PoW validation works\n"); printf("✅ Error handling works\n"); } else { printf("❌ SOME TESTS FAILED. Please review the output above.\n"); } nostr_cleanup(); return all_passed ? 0 : 1; }