// NIP-13 Proof of Work validation module #include #include "debug.h" #include #include #include #include "../nostr_core_lib/cjson/cJSON.h" #include "../nostr_core_lib/nostr_core/nostr_core.h" #include "../nostr_core_lib/nostr_core/nip013.h" #include "config.h" // Configuration functions from config.c extern int get_config_bool(const char* key, int default_value); extern int get_config_int(const char* key, int default_value); extern const char* get_config_value(const char* key); // Initialize PoW configuration using configuration system void init_pow_config() { // Configuration is now handled directly through database queries // No cache initialization needed } // Validate event Proof of Work according to NIP-13 int validate_event_pow(cJSON* event, char* error_message, size_t error_size) { // Get PoW configuration directly from database int enabled = get_config_bool("pow_enabled", 1); int min_pow_difficulty = get_config_int("pow_min_difficulty", 0); const char* pow_mode = get_config_value("pow_mode"); // Determine validation flags based on mode int validation_flags = NOSTR_POW_VALIDATE_BASIC; // Default if (pow_mode) { if (strcmp(pow_mode, "strict") == 0) { validation_flags = NOSTR_POW_VALIDATE_ANTI_SPAM | NOSTR_POW_STRICT_FORMAT; } else if (strcmp(pow_mode, "full") == 0) { validation_flags = NOSTR_POW_VALIDATE_FULL; } else if (strcmp(pow_mode, "basic") == 0) { validation_flags = NOSTR_POW_VALIDATE_BASIC; } else if (strcmp(pow_mode, "disabled") == 0) { enabled = 0; } free((char*)pow_mode); } if (!enabled) { return 0; // PoW validation disabled } if (!event) { snprintf(error_message, error_size, "pow: null event"); return NOSTR_ERROR_INVALID_INPUT; } // If min_pow_difficulty is 0, only validate events that have nonce tags // This allows events without PoW when difficulty requirement is 0 if (min_pow_difficulty == 0) { cJSON* tags = cJSON_GetObjectItem(event, "tags"); int has_nonce_tag = 0; if (tags && cJSON_IsArray(tags)) { cJSON* tag = NULL; cJSON_ArrayForEach(tag, tags) { if (cJSON_IsArray(tag) && cJSON_GetArraySize(tag) >= 2) { cJSON* tag_name = cJSON_GetArrayItem(tag, 0); if (cJSON_IsString(tag_name)) { const char* name = cJSON_GetStringValue(tag_name); if (name && strcmp(name, "nonce") == 0) { has_nonce_tag = 1; break; } } } } } // If no minimum difficulty required and no nonce tag, skip PoW validation if (!has_nonce_tag) { return 0; // Accept event without PoW when min_difficulty=0 } } // Perform PoW validation using nostr_core_lib nostr_pow_result_t pow_result; int validation_result = nostr_validate_pow(event, min_pow_difficulty, validation_flags, &pow_result); if (validation_result != NOSTR_SUCCESS) { // Handle specific error cases with appropriate messages switch (validation_result) { case NOSTR_ERROR_NIP13_INSUFFICIENT: snprintf(error_message, error_size, "pow: insufficient difficulty: %d < %d", pow_result.actual_difficulty, min_pow_difficulty); DEBUG_WARN("Event rejected: insufficient PoW difficulty"); break; case NOSTR_ERROR_NIP13_NO_NONCE_TAG: // This should not happen with min_difficulty=0 after our check above if (min_pow_difficulty > 0) { snprintf(error_message, error_size, "pow: missing required nonce tag"); DEBUG_WARN("Event rejected: missing nonce tag"); } else { return 0; // Allow when min_difficulty=0 } break; case NOSTR_ERROR_NIP13_INVALID_NONCE_TAG: snprintf(error_message, error_size, "pow: invalid nonce tag format"); DEBUG_WARN("Event rejected: invalid nonce tag format"); break; case NOSTR_ERROR_NIP13_TARGET_MISMATCH: snprintf(error_message, error_size, "pow: committed target (%d) lower than minimum (%d)", pow_result.committed_target, min_pow_difficulty); DEBUG_WARN("Event rejected: committed target too low (anti-spam protection)"); break; case NOSTR_ERROR_NIP13_CALCULATION: snprintf(error_message, error_size, "pow: difficulty calculation failed"); DEBUG_ERROR("PoW difficulty calculation error"); break; case NOSTR_ERROR_EVENT_INVALID_ID: snprintf(error_message, error_size, "pow: invalid event ID format"); DEBUG_WARN("Event rejected: invalid event ID for PoW calculation"); break; default: snprintf(error_message, error_size, "pow: validation failed - %s", strlen(pow_result.error_detail) > 0 ? pow_result.error_detail : "unknown error"); DEBUG_WARN("Event rejected: PoW validation failed"); } return validation_result; } return 0; // Success }