Nostr note validation added to nip01

This commit is contained in:
2025-08-19 06:59:04 -04:00
parent 1da4f6751e
commit 77d92dbcf9
17 changed files with 811 additions and 36 deletions

Binary file not shown.

BIN
tests/nip01_validation_test Executable file

Binary file not shown.

View File

@@ -0,0 +1,494 @@
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.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);
}
}
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;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.