Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
670329700c |
@@ -1,358 +0,0 @@
|
||||
# Event-Based Configuration System Implementation Plan
|
||||
|
||||
## Overview
|
||||
|
||||
This document provides a detailed implementation plan for transitioning the C Nostr Relay from command line arguments and file-based configuration to a pure event-based configuration system using kind 33334 Nostr events stored directly in the database.
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 0: File Structure Preparation ✅ COMPLETED
|
||||
|
||||
#### 0.1 Backup and Prepare Files ✅ COMPLETED
|
||||
**Actions:**
|
||||
1. ✅ Rename `src/config.c` to `src/config.c.old` - DONE
|
||||
2. ✅ Rename `src/config.h` to `src/config.h.old` - DONE
|
||||
3. ✅ Create new empty `src/config.c` and `src/config.h` - DONE
|
||||
4. ✅ Create new `src/default_config_event.h` - DONE
|
||||
|
||||
### Phase 1: Database Schema and Core Infrastructure ✅ COMPLETED
|
||||
|
||||
#### 1.1 Update Database Naming System ✅ COMPLETED
|
||||
**File:** `src/main.c`, new `src/config.c`, new `src/config.h`
|
||||
|
||||
```c
|
||||
// New functions implemented: ✅
|
||||
char* get_database_name_from_relay_pubkey(const char* relay_pubkey);
|
||||
int create_database_with_relay_pubkey(const char* relay_pubkey);
|
||||
```
|
||||
|
||||
**Changes Completed:** ✅
|
||||
- ✅ Create completely new `src/config.c` and `src/config.h` files
|
||||
- ✅ Rename old files to `src/config.c.old` and `src/config.h.old`
|
||||
- ✅ Modify `init_database()` to use relay pubkey for database naming
|
||||
- ✅ Use `nostr_core_lib` functions for all keypair generation
|
||||
- ✅ Database path: `./<relay_pubkey>.nrdb`
|
||||
- ✅ Remove all database path command line argument handling
|
||||
|
||||
#### 1.2 Configuration Event Storage ✅ COMPLETED
|
||||
**File:** new `src/config.c`, new `src/default_config_event.h`
|
||||
|
||||
```c
|
||||
// Configuration functions implemented: ✅
|
||||
int store_config_event_in_database(const cJSON* event);
|
||||
cJSON* load_config_event_from_database(const char* relay_pubkey);
|
||||
```
|
||||
|
||||
**Changes Completed:** ✅
|
||||
- ✅ Create new `src/default_config_event.h` for default configuration values
|
||||
- ✅ Add functions to store/retrieve kind 33334 events from events table
|
||||
- ✅ Use `nostr_core_lib` functions for all event validation
|
||||
- ✅ Clean separation: default config values isolated in header file
|
||||
- ✅ Remove existing config table dependencies
|
||||
|
||||
### Phase 2: Event Processing Integration ✅ COMPLETED
|
||||
|
||||
#### 2.1 Real-time Configuration Processing ✅ COMPLETED
|
||||
**File:** `src/main.c` (event processing functions)
|
||||
|
||||
**Integration Points:** ✅ IMPLEMENTED
|
||||
```c
|
||||
// In existing event processing loop: ✅ IMPLEMENTED
|
||||
// Added kind 33334 event detection in main event loop
|
||||
if (kind_num == 33334) {
|
||||
if (handle_configuration_event(event, error_message, sizeof(error_message)) == 0) {
|
||||
// Configuration event processed successfully
|
||||
}
|
||||
}
|
||||
|
||||
// Configuration event processing implemented: ✅
|
||||
int process_configuration_event(const cJSON* event);
|
||||
int handle_configuration_event(cJSON* event, char* error_message, size_t error_size);
|
||||
```
|
||||
|
||||
#### 2.2 Configuration Application System ⚠️ PARTIALLY COMPLETED
|
||||
**File:** `src/config.c`
|
||||
|
||||
**Status:** Configuration access functions implemented, field handlers need completion
|
||||
```c
|
||||
// Configuration access implemented: ✅
|
||||
const char* get_config_value(const char* key);
|
||||
int get_config_int(const char* key, int default_value);
|
||||
int get_config_bool(const char* key, int default_value);
|
||||
|
||||
// Field handlers need implementation: ⏳ IN PROGRESS
|
||||
// Need to implement specific apply functions for runtime changes
|
||||
```
|
||||
|
||||
### Phase 3: First-Time Startup System ✅ COMPLETED
|
||||
|
||||
#### 3.1 Key Generation and Initial Setup ✅ COMPLETED
|
||||
**File:** new `src/config.c`, `src/default_config_event.h`
|
||||
|
||||
**Status:** ✅ FULLY IMPLEMENTED with secure /dev/urandom + nostr_core_lib validation
|
||||
|
||||
```c
|
||||
int first_time_startup_sequence() {
|
||||
// 1. Generate admin keypair using nostr_core_lib
|
||||
unsigned char admin_privkey_bytes[32];
|
||||
char admin_privkey[65], admin_pubkey[65];
|
||||
|
||||
if (nostr_generate_private_key(admin_privkey_bytes) != 0) {
|
||||
return -1;
|
||||
}
|
||||
nostr_bytes_to_hex(admin_privkey_bytes, 32, admin_privkey);
|
||||
|
||||
unsigned char admin_pubkey_bytes[32];
|
||||
if (nostr_ec_public_key_from_private_key(admin_privkey_bytes, admin_pubkey_bytes) != 0) {
|
||||
return -1;
|
||||
}
|
||||
nostr_bytes_to_hex(admin_pubkey_bytes, 32, admin_pubkey);
|
||||
|
||||
// 2. Generate relay keypair using nostr_core_lib
|
||||
unsigned char relay_privkey_bytes[32];
|
||||
char relay_privkey[65], relay_pubkey[65];
|
||||
|
||||
if (nostr_generate_private_key(relay_privkey_bytes) != 0) {
|
||||
return -1;
|
||||
}
|
||||
nostr_bytes_to_hex(relay_privkey_bytes, 32, relay_privkey);
|
||||
|
||||
unsigned char relay_pubkey_bytes[32];
|
||||
if (nostr_ec_public_key_from_private_key(relay_privkey_bytes, relay_pubkey_bytes) != 0) {
|
||||
return -1;
|
||||
}
|
||||
nostr_bytes_to_hex(relay_pubkey_bytes, 32, relay_pubkey);
|
||||
|
||||
// 3. Create database with relay pubkey name
|
||||
if (create_database_with_relay_pubkey(relay_pubkey) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 4. Create initial configuration event using defaults from header
|
||||
cJSON* config_event = create_default_config_event(admin_privkey_bytes, relay_privkey, relay_pubkey);
|
||||
|
||||
// 5. Store configuration event in database
|
||||
store_config_event_in_database(config_event);
|
||||
|
||||
// 6. Print admin private key for user to save
|
||||
printf("=== SAVE THIS ADMIN PRIVATE KEY ===\n");
|
||||
printf("Admin Private Key: %s\n", admin_privkey);
|
||||
printf("===================================\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 Database Detection Logic ✅ COMPLETED
|
||||
**File:** `src/main.c`
|
||||
|
||||
**Status:** ✅ FULLY IMPLEMENTED
|
||||
```c
|
||||
// Implemented functions: ✅
|
||||
char** find_existing_nrdb_files(void);
|
||||
char* extract_pubkey_from_filename(const char* filename);
|
||||
int is_first_time_startup(void);
|
||||
int first_time_startup_sequence(void);
|
||||
int startup_existing_relay(const char* relay_pubkey);
|
||||
```
|
||||
|
||||
### Phase 4: Legacy System Removal ✅ PARTIALLY COMPLETED
|
||||
|
||||
#### 4.1 Remove Command Line Arguments ✅ COMPLETED
|
||||
**File:** `src/main.c`
|
||||
|
||||
**Status:** ✅ COMPLETED
|
||||
- ✅ All argument parsing logic removed except --help and --version
|
||||
- ✅ `--port`, `--config-dir`, `--config-file`, `--database-path` handling removed
|
||||
- ✅ Environment variable override systems removed
|
||||
- ✅ Clean help and version functions implemented
|
||||
|
||||
#### 4.2 Remove Configuration File System ✅ COMPLETED
|
||||
**File:** `src/config.c`
|
||||
|
||||
**Status:** ✅ COMPLETED - New file created from scratch
|
||||
- ✅ All legacy file-based configuration functions removed
|
||||
- ✅ XDG configuration directory logic removed
|
||||
- ✅ Pure event-based system implemented
|
||||
|
||||
#### 4.3 Remove Legacy Database Tables ⏳ PENDING
|
||||
**File:** `src/sql_schema.h`
|
||||
|
||||
**Status:** ⏳ NEEDS COMPLETION
|
||||
```sql
|
||||
-- Still need to remove these tables:
|
||||
DROP TABLE IF EXISTS config;
|
||||
DROP TABLE IF EXISTS config_history;
|
||||
DROP TABLE IF EXISTS config_file_cache;
|
||||
DROP VIEW IF EXISTS active_config;
|
||||
```
|
||||
|
||||
### Phase 5: Configuration Management
|
||||
|
||||
#### 5.1 Configuration Field Mapping
|
||||
**File:** `src/config.c`
|
||||
|
||||
```c
|
||||
// Map configuration tags to current system
|
||||
static const config_field_handler_t config_handlers[] = {
|
||||
{"auth_enabled", 0, apply_auth_enabled},
|
||||
{"relay_port", 1, apply_relay_port}, // requires restart
|
||||
{"max_connections", 0, apply_max_connections},
|
||||
{"relay_description", 0, apply_relay_description},
|
||||
{"relay_contact", 0, apply_relay_contact},
|
||||
{"relay_pubkey", 1, apply_relay_pubkey}, // requires restart
|
||||
{"relay_privkey", 1, apply_relay_privkey}, // requires restart
|
||||
{"pow_min_difficulty", 0, apply_pow_difficulty},
|
||||
{"nip40_expiration_enabled", 0, apply_expiration_enabled},
|
||||
{"max_subscriptions_per_client", 0, apply_max_subscriptions},
|
||||
{"max_event_tags", 0, apply_max_event_tags},
|
||||
{"max_content_length", 0, apply_max_content_length},
|
||||
{"default_limit", 0, apply_default_limit},
|
||||
{"max_limit", 0, apply_max_limit},
|
||||
// ... etc
|
||||
};
|
||||
```
|
||||
|
||||
#### 5.2 Startup Configuration Loading
|
||||
**File:** `src/main.c`
|
||||
|
||||
```c
|
||||
int startup_existing_relay(const char* relay_pubkey) {
|
||||
// 1. Open database
|
||||
if (init_database_with_pubkey(relay_pubkey) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 2. Load configuration event from database
|
||||
cJSON* config_event = load_config_event_from_database(relay_pubkey);
|
||||
if (!config_event) {
|
||||
log_error("No configuration event found in database");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 3. Apply all configuration from event
|
||||
if (apply_configuration_from_event(config_event) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 4. Continue with normal startup
|
||||
return start_relay_services();
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Order - PROGRESS STATUS
|
||||
|
||||
### Step 1: Core Infrastructure ✅ COMPLETED
|
||||
1. ✅ Implement database naming with relay pubkey
|
||||
2. ✅ Add key generation functions using `nostr_core_lib`
|
||||
3. ✅ Create configuration event storage/retrieval functions
|
||||
4. ✅ Test basic event creation and storage
|
||||
|
||||
### Step 2: Event Processing Integration ✅ MOSTLY COMPLETED
|
||||
1. ✅ Add kind 33334 event detection to event processing loop
|
||||
2. ✅ Implement configuration event validation
|
||||
3. ⚠️ Create configuration application handlers (basic access implemented, runtime handlers pending)
|
||||
4. ⏳ Test real-time configuration updates (infrastructure ready)
|
||||
|
||||
### Step 3: First-Time Startup ✅ COMPLETED
|
||||
1. ✅ Implement first-time startup detection
|
||||
2. ✅ Add automatic key generation and database creation
|
||||
3. ✅ Create default configuration event generation
|
||||
4. ✅ Test complete first-time startup flow
|
||||
|
||||
### Step 4: Legacy Removal ⚠️ MOSTLY COMPLETED
|
||||
1. ✅ Remove command line argument parsing
|
||||
2. ✅ Remove configuration file system
|
||||
3. ⏳ Remove legacy database tables (pending)
|
||||
4. ✅ Update all references to use event-based config
|
||||
|
||||
### Step 5: Testing and Validation ⚠️ PARTIALLY COMPLETED
|
||||
1. ✅ Test complete startup flow (first time and existing)
|
||||
2. ⏳ Test configuration updates via events (infrastructure ready)
|
||||
3. ⚠️ Test error handling and recovery (basic error handling implemented)
|
||||
4. ⏳ Performance testing and optimization (pending)
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
### For Existing Installations
|
||||
Since the new system uses a completely different approach:
|
||||
|
||||
1. **No Automatic Migration**: The new system starts fresh
|
||||
2. **Manual Migration**: Users can manually copy configuration values
|
||||
3. **Documentation**: Provide clear migration instructions
|
||||
4. **Coexistence**: Old and new systems use different database names
|
||||
|
||||
### Migration Steps for Users
|
||||
1. Stop existing relay
|
||||
2. Note current configuration values
|
||||
3. Start new relay (generates keys and new database)
|
||||
4. Create kind 33334 event with desired configuration using admin private key
|
||||
5. Send event to relay to update configuration
|
||||
|
||||
## Testing Requirements
|
||||
|
||||
### Unit Tests
|
||||
- Key generation functions
|
||||
- Configuration event creation and validation
|
||||
- Database naming logic
|
||||
- Configuration application handlers
|
||||
|
||||
### Integration Tests
|
||||
- Complete first-time startup flow
|
||||
- Configuration update via events
|
||||
- Error handling scenarios
|
||||
- Database operations
|
||||
|
||||
### Performance Tests
|
||||
- Startup time comparison
|
||||
- Configuration update response time
|
||||
- Memory usage analysis
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Admin Private Key**: Never stored, only printed once
|
||||
2. **Event Validation**: All configuration events must be signed by admin
|
||||
3. **Database Security**: Relay database contains relay private key
|
||||
4. **Key Generation**: Use `nostr_core_lib` for cryptographically secure generation
|
||||
|
||||
## Files to Modify
|
||||
|
||||
### Major Changes
|
||||
- `src/main.c` - Startup logic, event processing, argument removal
|
||||
- `src/config.c` - Complete rewrite for event-based configuration
|
||||
- `src/config.h` - Update function signatures and structures
|
||||
- `src/sql_schema.h` - Remove config tables
|
||||
|
||||
### Minor Changes
|
||||
- `Makefile` - Remove any config file generation
|
||||
- `systemd/` - Update service files if needed
|
||||
- Documentation updates
|
||||
|
||||
## Backwards Compatibility
|
||||
|
||||
**Breaking Changes:**
|
||||
- Command line arguments removed (except --help, --version)
|
||||
- Configuration files no longer used
|
||||
- Database naming scheme changed
|
||||
- Configuration table removed
|
||||
|
||||
**Migration Required:** This is a breaking change that requires manual migration for existing installations.
|
||||
|
||||
## Success Criteria - CURRENT STATUS
|
||||
|
||||
1. ✅ **Zero Command Line Arguments**: Relay starts with just `./c-relay`
|
||||
2. ✅ **Automatic First-Time Setup**: Generates keys and database automatically
|
||||
3. ⚠️ **Real-Time Configuration**: Infrastructure ready, handlers need completion
|
||||
4. ✅ **Single Database File**: All configuration and data in one `.nrdb` file
|
||||
5. ⚠️ **Admin Control**: Event processing implemented, signature validation ready
|
||||
6. ⚠️ **Clean Codebase**: Most legacy code removed, database tables cleanup pending
|
||||
|
||||
## Risk Mitigation
|
||||
|
||||
1. **Backup Strategy**: Document manual backup procedures for relay database
|
||||
2. **Key Loss Recovery**: Document recovery procedures if admin key is lost
|
||||
3. **Testing Coverage**: Comprehensive test suite before deployment
|
||||
4. **Rollback Plan**: Keep old version available during transition period
|
||||
5. **Documentation**: Comprehensive user and developer documentation
|
||||
|
||||
This implementation plan provides a clear path from the current system to the new event-based configuration architecture while maintaining security and reliability.
|
||||
@@ -1,128 +0,0 @@
|
||||
# Startup Configuration Design Analysis
|
||||
|
||||
## Review of startup_config_design.md
|
||||
|
||||
### Key Design Principles Identified
|
||||
|
||||
1. **Zero Command Line Arguments**: Complete elimination of CLI arguments for true "quick start"
|
||||
2. **Event-Based Configuration**: Configuration stored as Nostr event (kind 33334) in events table
|
||||
3. **Self-Contained Database**: Database named after relay pubkey (`<pubkey>.nrdb`)
|
||||
4. **First-Time Setup**: Automatic key generation and initial configuration creation
|
||||
5. **Configuration Consistency**: Always read from event, never from hardcoded defaults
|
||||
|
||||
### Implementation Gaps and Specifications Needed
|
||||
|
||||
#### 1. Key Generation Process
|
||||
**Specification:**
|
||||
```
|
||||
First Startup Key Generation:
|
||||
1. Generate all keys on first startup (admin private/public, relay private/public)
|
||||
2. Use nostr_core_lib for key generation entropy
|
||||
3. Keys are encoded in hex format
|
||||
4. Print admin private key to stdout for user to save (never stored)
|
||||
5. Store admin public key, relay private key, and relay public key in configuration event
|
||||
6. Admin can later change the 33334 event to alter stored keys
|
||||
```
|
||||
|
||||
#### 2. Database Naming and Location
|
||||
**Specification:**
|
||||
```
|
||||
Database Naming:
|
||||
1. Database is named using relay pubkey: ./<relay_pubkey>.nrdb
|
||||
2. Database path structure: ./<relay_pubkey>.nrdb
|
||||
3. If database creation fails, program quits (can't run without database)
|
||||
4. c_nostr_relay.db should never exist in new system
|
||||
```
|
||||
|
||||
#### 3. Configuration Event Structure (Kind 33334)
|
||||
**Specification:**
|
||||
```
|
||||
Event Structure:
|
||||
- Kind: 33334 (parameterized replaceable event)
|
||||
- Event validation: Use nostr_core_lib to validate event
|
||||
- Event content field: "C Nostr Relay Configuration" (descriptive text)
|
||||
- Configuration update mechanism: TBD
|
||||
- Complete tag structure provided in configuration section below
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 4. Configuration Change Monitoring
|
||||
**Configuration Monitoring System:**
|
||||
```
|
||||
Every event that is received is checked to see if it is a kind 33334 event from the admin pubkey.
|
||||
If so, it is processed as a configuration update.
|
||||
```
|
||||
|
||||
#### 5. Error Handling and Recovery
|
||||
**Specification:**
|
||||
```
|
||||
Error Recovery Priority:
|
||||
1. Try to load latest valid config event
|
||||
2. Generate new default configuration event if none exists
|
||||
3. Exit with error if all recovery attempts fail
|
||||
|
||||
Note: There is only ever one configuration event (parameterized replaceable event),
|
||||
so no fallback to previous versions.
|
||||
```
|
||||
|
||||
### Design Clarifications
|
||||
|
||||
**Key Management:**
|
||||
- Admin private key is never stored, only printed once at first startup
|
||||
- Single admin system (no multi-admin support)
|
||||
- No key rotation support
|
||||
|
||||
**Configuration Management:**
|
||||
- No configuration versioning/timestamping
|
||||
- No automatic backup of configuration events
|
||||
- Configuration events are not broadcastable to other relays
|
||||
- Future: Auth system to restrict admin access to configuration events
|
||||
|
||||
---
|
||||
|
||||
## Complete Current Configuration Structure
|
||||
|
||||
Based on analysis of [`src/config.c`](src/config.c:753-795), here is the complete current configuration structure that will be converted to event tags:
|
||||
|
||||
### Complete Event Structure Example
|
||||
```json
|
||||
{
|
||||
"kind": 33334,
|
||||
"created_at": 1725661483,
|
||||
"tags": [
|
||||
["d", "<relay_pubkey>"],
|
||||
["auth_enabled", "false"],
|
||||
["relay_port", "8888"],
|
||||
["max_connections", "100"],
|
||||
|
||||
["relay_description", "High-performance C Nostr relay with SQLite storage"],
|
||||
["relay_contact", ""],
|
||||
["relay_pubkey", "<relay_public_key>"],
|
||||
["relay_privkey", "<relay_private_key>"],
|
||||
["relay_software", "https://git.laantungir.net/laantungir/c-relay.git"],
|
||||
["relay_version", "v1.0.0"],
|
||||
|
||||
["pow_min_difficulty", "0"],
|
||||
["pow_mode", "basic"],
|
||||
["nip40_expiration_enabled", "true"],
|
||||
["nip40_expiration_strict", "true"],
|
||||
["nip40_expiration_filter", "true"],
|
||||
["nip40_expiration_grace_period", "300"],
|
||||
["max_subscriptions_per_client", "25"],
|
||||
["max_total_subscriptions", "5000"],
|
||||
["max_filters_per_subscription", "10"],
|
||||
["max_event_tags", "100"],
|
||||
["max_content_length", "8196"],
|
||||
["max_message_length", "16384"],
|
||||
["default_limit", "500"],
|
||||
["max_limit", "5000"]
|
||||
],
|
||||
"content": "C Nostr Relay Configuration",
|
||||
"pubkey": "<admin_public_key>",
|
||||
"id": "<computed_event_id>",
|
||||
"sig": "<event_signature>"
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** The `admin_pubkey` tag is omitted as it's redundant with the event's `pubkey` field.
|
||||
870
src/config.c
870
src/config.c
File diff suppressed because it is too large
Load Diff
77
src/config.h
77
src/config.h
@@ -27,78 +27,6 @@ struct lws;
|
||||
// Database path for event-based config
|
||||
extern char g_database_path[512];
|
||||
|
||||
// Unified configuration cache structure (consolidates all caching systems)
|
||||
typedef struct {
|
||||
// Critical keys (frequently accessed)
|
||||
char admin_pubkey[65];
|
||||
char relay_pubkey[65];
|
||||
|
||||
// Auth config (from request_validator)
|
||||
int auth_required;
|
||||
long max_file_size;
|
||||
int admin_enabled;
|
||||
int nip42_mode;
|
||||
int nip42_challenge_timeout;
|
||||
int nip42_time_tolerance;
|
||||
int nip70_protected_events_enabled;
|
||||
|
||||
// Static buffer for config values (replaces static buffers in get_config_value functions)
|
||||
char temp_buffer[CONFIG_VALUE_MAX_LENGTH];
|
||||
|
||||
// NIP-11 relay information (migrated from g_relay_info in main.c)
|
||||
struct {
|
||||
char name[RELAY_NAME_MAX_LENGTH];
|
||||
char description[RELAY_DESCRIPTION_MAX_LENGTH];
|
||||
char banner[RELAY_URL_MAX_LENGTH];
|
||||
char icon[RELAY_URL_MAX_LENGTH];
|
||||
char pubkey[RELAY_PUBKEY_MAX_LENGTH];
|
||||
char contact[RELAY_CONTACT_MAX_LENGTH];
|
||||
char software[RELAY_URL_MAX_LENGTH];
|
||||
char version[64];
|
||||
char privacy_policy[RELAY_URL_MAX_LENGTH];
|
||||
char terms_of_service[RELAY_URL_MAX_LENGTH];
|
||||
// Raw string values for parsing into cJSON arrays
|
||||
char supported_nips_str[CONFIG_VALUE_MAX_LENGTH];
|
||||
char language_tags_str[CONFIG_VALUE_MAX_LENGTH];
|
||||
char relay_countries_str[CONFIG_VALUE_MAX_LENGTH];
|
||||
// Parsed cJSON arrays
|
||||
cJSON* supported_nips;
|
||||
cJSON* limitation;
|
||||
cJSON* retention;
|
||||
cJSON* relay_countries;
|
||||
cJSON* language_tags;
|
||||
cJSON* tags;
|
||||
char posting_policy[RELAY_URL_MAX_LENGTH];
|
||||
cJSON* fees;
|
||||
char payments_url[RELAY_URL_MAX_LENGTH];
|
||||
} relay_info;
|
||||
|
||||
// NIP-13 PoW configuration (migrated from g_pow_config in main.c)
|
||||
struct {
|
||||
int enabled;
|
||||
int min_pow_difficulty;
|
||||
int validation_flags;
|
||||
int require_nonce_tag;
|
||||
int reject_lower_targets;
|
||||
int strict_format;
|
||||
int anti_spam_mode;
|
||||
} pow_config;
|
||||
|
||||
// NIP-40 Expiration configuration (migrated from g_expiration_config in main.c)
|
||||
struct {
|
||||
int enabled;
|
||||
int strict_mode;
|
||||
int filter_responses;
|
||||
int delete_expired;
|
||||
long grace_period;
|
||||
} expiration_config;
|
||||
|
||||
// Cache management
|
||||
time_t cache_expires;
|
||||
int cache_valid;
|
||||
pthread_mutex_t cache_lock;
|
||||
} unified_config_cache_t;
|
||||
|
||||
// Command line options structure for first-time startup
|
||||
typedef struct {
|
||||
int port_override; // -1 = not set, >0 = port value
|
||||
@@ -108,9 +36,6 @@ typedef struct {
|
||||
int debug_level; // 0-5, default 0 (no debug output)
|
||||
} cli_options_t;
|
||||
|
||||
// Global unified configuration cache
|
||||
extern unified_config_cache_t g_unified_cache;
|
||||
|
||||
// Core configuration functions (temporary compatibility)
|
||||
int init_configuration_system(const char* config_dir_override, const char* config_file_override);
|
||||
void cleanup_configuration_system(void);
|
||||
@@ -138,7 +63,7 @@ int get_config_bool(const char* key, int default_value);
|
||||
|
||||
// First-time startup functions
|
||||
int is_first_time_startup(void);
|
||||
int first_time_startup_sequence(const cli_options_t* cli_options);
|
||||
int first_time_startup_sequence(const cli_options_t* cli_options, char* admin_pubkey_out, char* relay_pubkey_out, char* relay_privkey_out);
|
||||
int startup_existing_relay(const char* relay_pubkey, const cli_options_t* cli_options);
|
||||
|
||||
// Configuration application functions
|
||||
|
||||
@@ -28,6 +28,8 @@ static const struct {
|
||||
{"nip42_auth_required_subscriptions", "false"},
|
||||
{"nip42_auth_required_kinds", "4,14"}, // Default: DM kinds require auth
|
||||
{"nip42_challenge_expiration", "600"}, // 10 minutes
|
||||
{"nip42_challenge_timeout", "600"}, // Challenge timeout (seconds)
|
||||
{"nip42_time_tolerance", "300"}, // Time tolerance (seconds)
|
||||
|
||||
// NIP-70 Protected Events
|
||||
{"nip70_protected_events_enabled", "false"},
|
||||
|
||||
49
src/main.c
49
src/main.c
@@ -74,10 +74,6 @@ struct relay_info {
|
||||
char payments_url[RELAY_URL_MAX_LENGTH];
|
||||
};
|
||||
|
||||
// Global relay information instance moved to unified cache
|
||||
// static struct relay_info g_relay_info = {0}; // REMOVED - now in g_unified_cache.relay_info
|
||||
|
||||
|
||||
// NIP-40 Expiration configuration (now in nip040.c)
|
||||
extern struct expiration_config g_expiration_config;
|
||||
|
||||
@@ -92,12 +88,6 @@ extern subscription_manager_t g_subscription_manager;
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Forward declarations for logging functions - REMOVED (replaced by debug system)
|
||||
|
||||
// Forward declaration for subscription manager configuration
|
||||
void update_subscription_manager_config(void);
|
||||
|
||||
@@ -1273,10 +1263,8 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
cJSON_AddItemToObject(event, "tags", tags);
|
||||
|
||||
// Check expiration filtering (NIP-40) at application level
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
int expiration_enabled = g_unified_cache.expiration_config.enabled;
|
||||
int filter_responses = g_unified_cache.expiration_config.filter_responses;
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
int expiration_enabled = get_config_bool("expiration_enabled", 1);
|
||||
int filter_responses = get_config_bool("expiration_filter", 1);
|
||||
|
||||
if (expiration_enabled && filter_responses) {
|
||||
time_t current_time = time(NULL);
|
||||
@@ -1632,7 +1620,10 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
// Run first-time startup sequence (generates keys, sets up database path, but doesn't store private key yet)
|
||||
if (first_time_startup_sequence(&cli_options) != 0) {
|
||||
char admin_pubkey[65] = {0};
|
||||
char relay_pubkey[65] = {0};
|
||||
char relay_privkey[65] = {0};
|
||||
if (first_time_startup_sequence(&cli_options, admin_pubkey, relay_pubkey, relay_privkey) != 0) {
|
||||
DEBUG_ERROR("Failed to complete first-time startup sequence");
|
||||
cleanup_configuration_system();
|
||||
nostr_cleanup();
|
||||
@@ -1662,19 +1653,43 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
// DEBUG_GUARD_END
|
||||
|
||||
// Now that database is available, populate the complete config table atomically
|
||||
// BUG FIX: Use the pubkeys returned from first_time_startup_sequence instead of trying to read from empty database
|
||||
DEBUG_LOG("Using pubkeys from first-time startup sequence for config population");
|
||||
DEBUG_LOG("admin_pubkey from startup: %s", admin_pubkey);
|
||||
DEBUG_LOG("relay_pubkey from startup: %s", relay_pubkey);
|
||||
|
||||
if (populate_all_config_values_atomic(admin_pubkey, relay_pubkey) != 0) {
|
||||
DEBUG_ERROR("Failed to populate complete config table");
|
||||
cleanup_configuration_system();
|
||||
nostr_cleanup();
|
||||
close_database();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Apply CLI overrides atomically (after complete config table exists)
|
||||
if (apply_cli_overrides_atomic(&cli_options) != 0) {
|
||||
DEBUG_ERROR("Failed to apply CLI overrides");
|
||||
cleanup_configuration_system();
|
||||
nostr_cleanup();
|
||||
close_database();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Now that database is available, store the relay private key securely
|
||||
const char* relay_privkey = get_temp_relay_private_key();
|
||||
if (relay_privkey) {
|
||||
if (relay_privkey[0] != '\0') {
|
||||
if (store_relay_private_key(relay_privkey) != 0) {
|
||||
DEBUG_ERROR("Failed to store relay private key securely after database initialization");
|
||||
cleanup_configuration_system();
|
||||
nostr_cleanup();
|
||||
close_database();
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
DEBUG_ERROR("Relay private key not available from first-time startup");
|
||||
cleanup_configuration_system();
|
||||
nostr_cleanup();
|
||||
close_database();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
556
src/nip011.c
556
src/nip011.c
@@ -15,8 +15,7 @@ const char* get_config_value(const char* key);
|
||||
int get_config_int(const char* key, int default_value);
|
||||
int get_config_bool(const char* key, int default_value);
|
||||
|
||||
// Forward declarations for global cache access
|
||||
extern unified_config_cache_t g_unified_cache;
|
||||
// NIP-11 relay information is now managed directly from config table
|
||||
|
||||
// Forward declarations for constants (defined in config.h and other headers)
|
||||
#define HTTP_STATUS_OK 200
|
||||
@@ -75,185 +74,14 @@ cJSON* parse_comma_separated_array(const char* csv_string) {
|
||||
|
||||
// Initialize relay information using configuration system
|
||||
void init_relay_info() {
|
||||
// Get all config values first (without holding mutex to avoid deadlock)
|
||||
// Note: These may be dynamically allocated strings that need to be freed
|
||||
const char* relay_name = get_config_value("relay_name");
|
||||
const char* relay_description = get_config_value("relay_description");
|
||||
const char* relay_software = get_config_value("relay_software");
|
||||
const char* relay_version = get_config_value("relay_version");
|
||||
const char* relay_contact = get_config_value("relay_contact");
|
||||
const char* relay_pubkey = get_config_value("relay_pubkey");
|
||||
const char* supported_nips_csv = get_config_value("supported_nips");
|
||||
const char* language_tags_csv = get_config_value("language_tags");
|
||||
const char* relay_countries_csv = get_config_value("relay_countries");
|
||||
const char* posting_policy = get_config_value("posting_policy");
|
||||
const char* payments_url = get_config_value("payments_url");
|
||||
|
||||
// Get config values for limitations
|
||||
int max_message_length = get_config_int("max_message_length", 16384);
|
||||
int max_subscriptions_per_client = get_config_int("max_subscriptions_per_client", 20);
|
||||
int max_limit = get_config_int("max_limit", 5000);
|
||||
int max_event_tags = get_config_int("max_event_tags", 100);
|
||||
int max_content_length = get_config_int("max_content_length", 8196);
|
||||
int default_limit = get_config_int("default_limit", 500);
|
||||
int admin_enabled = get_config_bool("admin_enabled", 0);
|
||||
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
|
||||
// Update relay information fields
|
||||
if (relay_name) {
|
||||
strncpy(g_unified_cache.relay_info.name, relay_name, sizeof(g_unified_cache.relay_info.name) - 1);
|
||||
free((char*)relay_name); // Free dynamically allocated string
|
||||
} else {
|
||||
strncpy(g_unified_cache.relay_info.name, "C Nostr Relay", sizeof(g_unified_cache.relay_info.name) - 1);
|
||||
}
|
||||
|
||||
if (relay_description) {
|
||||
strncpy(g_unified_cache.relay_info.description, relay_description, sizeof(g_unified_cache.relay_info.description) - 1);
|
||||
free((char*)relay_description); // Free dynamically allocated string
|
||||
} else {
|
||||
strncpy(g_unified_cache.relay_info.description, "A high-performance Nostr relay implemented in C with SQLite storage", sizeof(g_unified_cache.relay_info.description) - 1);
|
||||
}
|
||||
|
||||
if (relay_software) {
|
||||
strncpy(g_unified_cache.relay_info.software, relay_software, sizeof(g_unified_cache.relay_info.software) - 1);
|
||||
free((char*)relay_software); // Free dynamically allocated string
|
||||
} else {
|
||||
strncpy(g_unified_cache.relay_info.software, "https://git.laantungir.net/laantungir/c-relay.git", sizeof(g_unified_cache.relay_info.software) - 1);
|
||||
}
|
||||
|
||||
if (relay_version) {
|
||||
strncpy(g_unified_cache.relay_info.version, relay_version, sizeof(g_unified_cache.relay_info.version) - 1);
|
||||
free((char*)relay_version); // Free dynamically allocated string
|
||||
} else {
|
||||
strncpy(g_unified_cache.relay_info.version, "0.2.0", sizeof(g_unified_cache.relay_info.version) - 1);
|
||||
}
|
||||
|
||||
if (relay_contact) {
|
||||
strncpy(g_unified_cache.relay_info.contact, relay_contact, sizeof(g_unified_cache.relay_info.contact) - 1);
|
||||
free((char*)relay_contact); // Free dynamically allocated string
|
||||
}
|
||||
|
||||
if (relay_pubkey) {
|
||||
strncpy(g_unified_cache.relay_info.pubkey, relay_pubkey, sizeof(g_unified_cache.relay_info.pubkey) - 1);
|
||||
free((char*)relay_pubkey); // Free dynamically allocated string
|
||||
}
|
||||
|
||||
if (posting_policy) {
|
||||
strncpy(g_unified_cache.relay_info.posting_policy, posting_policy, sizeof(g_unified_cache.relay_info.posting_policy) - 1);
|
||||
free((char*)posting_policy); // Free dynamically allocated string
|
||||
}
|
||||
|
||||
if (payments_url) {
|
||||
strncpy(g_unified_cache.relay_info.payments_url, payments_url, sizeof(g_unified_cache.relay_info.payments_url) - 1);
|
||||
free((char*)payments_url); // Free dynamically allocated string
|
||||
}
|
||||
|
||||
// Initialize supported NIPs array from config
|
||||
if (supported_nips_csv) {
|
||||
g_unified_cache.relay_info.supported_nips = parse_comma_separated_array(supported_nips_csv);
|
||||
free((char*)supported_nips_csv); // Free dynamically allocated string
|
||||
} else {
|
||||
// Fallback to default supported NIPs
|
||||
g_unified_cache.relay_info.supported_nips = cJSON_CreateArray();
|
||||
if (g_unified_cache.relay_info.supported_nips) {
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(1)); // NIP-01: Basic protocol
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(9)); // NIP-09: Event deletion
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(11)); // NIP-11: Relay information
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(13)); // NIP-13: Proof of Work
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(15)); // NIP-15: EOSE
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(20)); // NIP-20: Command results
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(40)); // NIP-40: Expiration Timestamp
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(42)); // NIP-42: Authentication
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize server limitations using configuration
|
||||
g_unified_cache.relay_info.limitation = cJSON_CreateObject();
|
||||
if (g_unified_cache.relay_info.limitation) {
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_message_length", max_message_length);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_subscriptions", max_subscriptions_per_client);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_limit", max_limit);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_subid_length", SUBSCRIPTION_ID_MAX_LENGTH);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_event_tags", max_event_tags);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_content_length", max_content_length);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "min_pow_difficulty", g_unified_cache.pow_config.min_pow_difficulty);
|
||||
cJSON_AddBoolToObject(g_unified_cache.relay_info.limitation, "auth_required", admin_enabled ? cJSON_True : cJSON_False);
|
||||
cJSON_AddBoolToObject(g_unified_cache.relay_info.limitation, "payment_required", cJSON_False);
|
||||
cJSON_AddBoolToObject(g_unified_cache.relay_info.limitation, "restricted_writes", cJSON_False);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "created_at_lower_limit", 0);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "created_at_upper_limit", 2147483647);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "default_limit", default_limit);
|
||||
}
|
||||
|
||||
// Initialize empty retention policies (can be configured later)
|
||||
g_unified_cache.relay_info.retention = cJSON_CreateArray();
|
||||
|
||||
// Initialize language tags from config
|
||||
if (language_tags_csv) {
|
||||
g_unified_cache.relay_info.language_tags = parse_comma_separated_array(language_tags_csv);
|
||||
free((char*)language_tags_csv); // Free dynamically allocated string
|
||||
} else {
|
||||
// Fallback to global
|
||||
g_unified_cache.relay_info.language_tags = cJSON_CreateArray();
|
||||
if (g_unified_cache.relay_info.language_tags) {
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.language_tags, cJSON_CreateString("*"));
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize relay countries from config
|
||||
if (relay_countries_csv) {
|
||||
g_unified_cache.relay_info.relay_countries = parse_comma_separated_array(relay_countries_csv);
|
||||
free((char*)relay_countries_csv); // Free dynamically allocated string
|
||||
} else {
|
||||
// Fallback to global
|
||||
g_unified_cache.relay_info.relay_countries = cJSON_CreateArray();
|
||||
if (g_unified_cache.relay_info.relay_countries) {
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.relay_countries, cJSON_CreateString("*"));
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize content tags as empty array
|
||||
g_unified_cache.relay_info.tags = cJSON_CreateArray();
|
||||
|
||||
// Initialize fees as empty object (no payment required by default)
|
||||
g_unified_cache.relay_info.fees = cJSON_CreateObject();
|
||||
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
// NIP-11 relay information is now generated dynamically from config table
|
||||
// No initialization needed - data is fetched directly from database when requested
|
||||
}
|
||||
|
||||
// Clean up relay information JSON objects
|
||||
void cleanup_relay_info() {
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
if (g_unified_cache.relay_info.supported_nips) {
|
||||
cJSON_Delete(g_unified_cache.relay_info.supported_nips);
|
||||
g_unified_cache.relay_info.supported_nips = NULL;
|
||||
}
|
||||
if (g_unified_cache.relay_info.limitation) {
|
||||
cJSON_Delete(g_unified_cache.relay_info.limitation);
|
||||
g_unified_cache.relay_info.limitation = NULL;
|
||||
}
|
||||
if (g_unified_cache.relay_info.retention) {
|
||||
cJSON_Delete(g_unified_cache.relay_info.retention);
|
||||
g_unified_cache.relay_info.retention = NULL;
|
||||
}
|
||||
if (g_unified_cache.relay_info.language_tags) {
|
||||
cJSON_Delete(g_unified_cache.relay_info.language_tags);
|
||||
g_unified_cache.relay_info.language_tags = NULL;
|
||||
}
|
||||
if (g_unified_cache.relay_info.relay_countries) {
|
||||
cJSON_Delete(g_unified_cache.relay_info.relay_countries);
|
||||
g_unified_cache.relay_info.relay_countries = NULL;
|
||||
}
|
||||
if (g_unified_cache.relay_info.tags) {
|
||||
cJSON_Delete(g_unified_cache.relay_info.tags);
|
||||
g_unified_cache.relay_info.tags = NULL;
|
||||
}
|
||||
if (g_unified_cache.relay_info.fees) {
|
||||
cJSON_Delete(g_unified_cache.relay_info.fees);
|
||||
g_unified_cache.relay_info.fees = NULL;
|
||||
}
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
// NIP-11 relay information is now generated dynamically from config table
|
||||
// No cleanup needed - data is fetched directly from database when requested
|
||||
}
|
||||
|
||||
// Generate NIP-11 compliant JSON document
|
||||
@@ -264,248 +92,194 @@ cJSON* generate_relay_info_json() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
// Get all config values directly from database
|
||||
const char* relay_name = get_config_value("relay_name");
|
||||
const char* relay_description = get_config_value("relay_description");
|
||||
const char* relay_banner = get_config_value("relay_banner");
|
||||
const char* relay_icon = get_config_value("relay_icon");
|
||||
const char* relay_pubkey = get_config_value("relay_pubkey");
|
||||
const char* relay_contact = get_config_value("relay_contact");
|
||||
const char* supported_nips_csv = get_config_value("supported_nips");
|
||||
const char* relay_software = get_config_value("relay_software");
|
||||
const char* relay_version = get_config_value("relay_version");
|
||||
const char* privacy_policy = get_config_value("privacy_policy");
|
||||
const char* terms_of_service = get_config_value("terms_of_service");
|
||||
const char* posting_policy = get_config_value("posting_policy");
|
||||
const char* language_tags_csv = get_config_value("language_tags");
|
||||
const char* relay_countries_csv = get_config_value("relay_countries");
|
||||
const char* payments_url = get_config_value("payments_url");
|
||||
|
||||
// Defensive reinit: if relay_info appears empty (cache refresh wiped it), rebuild it directly from table
|
||||
if (strlen(g_unified_cache.relay_info.name) == 0 &&
|
||||
strlen(g_unified_cache.relay_info.description) == 0 &&
|
||||
strlen(g_unified_cache.relay_info.software) == 0) {
|
||||
DEBUG_WARN("NIP-11 relay_info appears empty, rebuilding directly from config table");
|
||||
|
||||
// Rebuild relay_info directly from config table to avoid circular cache dependency
|
||||
// Get values directly from table (similar to init_relay_info but without cache calls)
|
||||
const char* relay_name = get_config_value_from_table("relay_name");
|
||||
if (relay_name) {
|
||||
strncpy(g_unified_cache.relay_info.name, relay_name, sizeof(g_unified_cache.relay_info.name) - 1);
|
||||
free((char*)relay_name);
|
||||
} else {
|
||||
strncpy(g_unified_cache.relay_info.name, "C Nostr Relay", sizeof(g_unified_cache.relay_info.name) - 1);
|
||||
}
|
||||
|
||||
const char* relay_description = get_config_value_from_table("relay_description");
|
||||
if (relay_description) {
|
||||
strncpy(g_unified_cache.relay_info.description, relay_description, sizeof(g_unified_cache.relay_info.description) - 1);
|
||||
free((char*)relay_description);
|
||||
} else {
|
||||
strncpy(g_unified_cache.relay_info.description, "A high-performance Nostr relay implemented in C with SQLite storage", sizeof(g_unified_cache.relay_info.description) - 1);
|
||||
}
|
||||
|
||||
const char* relay_software = get_config_value_from_table("relay_software");
|
||||
if (relay_software) {
|
||||
strncpy(g_unified_cache.relay_info.software, relay_software, sizeof(g_unified_cache.relay_info.software) - 1);
|
||||
free((char*)relay_software);
|
||||
} else {
|
||||
strncpy(g_unified_cache.relay_info.software, "https://git.laantungir.net/laantungir/c-relay.git", sizeof(g_unified_cache.relay_info.software) - 1);
|
||||
}
|
||||
|
||||
const char* relay_version = get_config_value_from_table("relay_version");
|
||||
if (relay_version) {
|
||||
strncpy(g_unified_cache.relay_info.version, relay_version, sizeof(g_unified_cache.relay_info.version) - 1);
|
||||
free((char*)relay_version);
|
||||
} else {
|
||||
strncpy(g_unified_cache.relay_info.version, "0.2.0", sizeof(g_unified_cache.relay_info.version) - 1);
|
||||
}
|
||||
|
||||
const char* relay_contact = get_config_value_from_table("relay_contact");
|
||||
if (relay_contact) {
|
||||
strncpy(g_unified_cache.relay_info.contact, relay_contact, sizeof(g_unified_cache.relay_info.contact) - 1);
|
||||
free((char*)relay_contact);
|
||||
}
|
||||
|
||||
const char* relay_pubkey = get_config_value_from_table("relay_pubkey");
|
||||
if (relay_pubkey) {
|
||||
strncpy(g_unified_cache.relay_info.pubkey, relay_pubkey, sizeof(g_unified_cache.relay_info.pubkey) - 1);
|
||||
free((char*)relay_pubkey);
|
||||
}
|
||||
|
||||
const char* posting_policy = get_config_value_from_table("posting_policy");
|
||||
if (posting_policy) {
|
||||
strncpy(g_unified_cache.relay_info.posting_policy, posting_policy, sizeof(g_unified_cache.relay_info.posting_policy) - 1);
|
||||
free((char*)posting_policy);
|
||||
}
|
||||
|
||||
const char* payments_url = get_config_value_from_table("payments_url");
|
||||
if (payments_url) {
|
||||
strncpy(g_unified_cache.relay_info.payments_url, payments_url, sizeof(g_unified_cache.relay_info.payments_url) - 1);
|
||||
free((char*)payments_url);
|
||||
}
|
||||
|
||||
// Rebuild supported_nips array
|
||||
const char* supported_nips_csv = get_config_value_from_table("supported_nips");
|
||||
if (supported_nips_csv) {
|
||||
g_unified_cache.relay_info.supported_nips = parse_comma_separated_array(supported_nips_csv);
|
||||
free((char*)supported_nips_csv);
|
||||
} else {
|
||||
g_unified_cache.relay_info.supported_nips = cJSON_CreateArray();
|
||||
if (g_unified_cache.relay_info.supported_nips) {
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(1));
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(9));
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(11));
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(13));
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(15));
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(20));
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(40));
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(42));
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild limitation object
|
||||
int max_message_length = 16384;
|
||||
const char* max_msg_str = get_config_value_from_table("max_message_length");
|
||||
if (max_msg_str) {
|
||||
max_message_length = atoi(max_msg_str);
|
||||
free((char*)max_msg_str);
|
||||
}
|
||||
|
||||
int max_subscriptions_per_client = 20;
|
||||
const char* max_subs_str = get_config_value_from_table("max_subscriptions_per_client");
|
||||
if (max_subs_str) {
|
||||
max_subscriptions_per_client = atoi(max_subs_str);
|
||||
free((char*)max_subs_str);
|
||||
}
|
||||
|
||||
int max_limit = 5000;
|
||||
const char* max_limit_str = get_config_value_from_table("max_limit");
|
||||
if (max_limit_str) {
|
||||
max_limit = atoi(max_limit_str);
|
||||
free((char*)max_limit_str);
|
||||
}
|
||||
|
||||
int max_event_tags = 100;
|
||||
const char* max_tags_str = get_config_value_from_table("max_event_tags");
|
||||
if (max_tags_str) {
|
||||
max_event_tags = atoi(max_tags_str);
|
||||
free((char*)max_tags_str);
|
||||
}
|
||||
|
||||
int max_content_length = 8196;
|
||||
const char* max_content_str = get_config_value_from_table("max_content_length");
|
||||
if (max_content_str) {
|
||||
max_content_length = atoi(max_content_str);
|
||||
free((char*)max_content_str);
|
||||
}
|
||||
|
||||
int default_limit = 500;
|
||||
const char* default_limit_str = get_config_value_from_table("default_limit");
|
||||
if (default_limit_str) {
|
||||
default_limit = atoi(default_limit_str);
|
||||
free((char*)default_limit_str);
|
||||
}
|
||||
|
||||
int admin_enabled = 0;
|
||||
const char* admin_enabled_str = get_config_value_from_table("admin_enabled");
|
||||
if (admin_enabled_str) {
|
||||
admin_enabled = (strcmp(admin_enabled_str, "true") == 0) ? 1 : 0;
|
||||
free((char*)admin_enabled_str);
|
||||
}
|
||||
|
||||
g_unified_cache.relay_info.limitation = cJSON_CreateObject();
|
||||
if (g_unified_cache.relay_info.limitation) {
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_message_length", max_message_length);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_subscriptions", max_subscriptions_per_client);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_limit", max_limit);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_subid_length", SUBSCRIPTION_ID_MAX_LENGTH);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_event_tags", max_event_tags);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_content_length", max_content_length);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "min_pow_difficulty", g_unified_cache.pow_config.min_pow_difficulty);
|
||||
cJSON_AddBoolToObject(g_unified_cache.relay_info.limitation, "auth_required", admin_enabled ? cJSON_True : cJSON_False);
|
||||
cJSON_AddBoolToObject(g_unified_cache.relay_info.limitation, "payment_required", cJSON_False);
|
||||
cJSON_AddBoolToObject(g_unified_cache.relay_info.limitation, "restricted_writes", cJSON_False);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "created_at_lower_limit", 0);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "created_at_upper_limit", 2147483647);
|
||||
cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "default_limit", default_limit);
|
||||
}
|
||||
|
||||
// Rebuild other arrays (empty for now)
|
||||
g_unified_cache.relay_info.retention = cJSON_CreateArray();
|
||||
g_unified_cache.relay_info.language_tags = cJSON_CreateArray();
|
||||
if (g_unified_cache.relay_info.language_tags) {
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.language_tags, cJSON_CreateString("*"));
|
||||
}
|
||||
g_unified_cache.relay_info.relay_countries = cJSON_CreateArray();
|
||||
if (g_unified_cache.relay_info.relay_countries) {
|
||||
cJSON_AddItemToArray(g_unified_cache.relay_info.relay_countries, cJSON_CreateString("*"));
|
||||
}
|
||||
g_unified_cache.relay_info.tags = cJSON_CreateArray();
|
||||
g_unified_cache.relay_info.fees = cJSON_CreateObject();
|
||||
|
||||
}
|
||||
// Get config values for limitations
|
||||
int max_message_length = get_config_int("max_message_length", 16384);
|
||||
int max_subscriptions_per_client = get_config_int("max_subscriptions_per_client", 20);
|
||||
int max_limit = get_config_int("max_limit", 5000);
|
||||
int max_event_tags = get_config_int("max_event_tags", 100);
|
||||
int max_content_length = get_config_int("max_content_length", 8196);
|
||||
int default_limit = get_config_int("default_limit", 500);
|
||||
int min_pow_difficulty = get_config_int("pow_min_difficulty", 0);
|
||||
int admin_enabled = get_config_bool("admin_enabled", 0);
|
||||
|
||||
// Add basic relay information
|
||||
if (strlen(g_unified_cache.relay_info.name) > 0) {
|
||||
cJSON_AddStringToObject(info, "name", g_unified_cache.relay_info.name);
|
||||
if (relay_name && strlen(relay_name) > 0) {
|
||||
cJSON_AddStringToObject(info, "name", relay_name);
|
||||
free((char*)relay_name);
|
||||
} else {
|
||||
cJSON_AddStringToObject(info, "name", "C Nostr Relay");
|
||||
}
|
||||
if (strlen(g_unified_cache.relay_info.description) > 0) {
|
||||
cJSON_AddStringToObject(info, "description", g_unified_cache.relay_info.description);
|
||||
|
||||
if (relay_description && strlen(relay_description) > 0) {
|
||||
cJSON_AddStringToObject(info, "description", relay_description);
|
||||
free((char*)relay_description);
|
||||
} else {
|
||||
cJSON_AddStringToObject(info, "description", "A high-performance Nostr relay implemented in C with SQLite storage");
|
||||
}
|
||||
if (strlen(g_unified_cache.relay_info.banner) > 0) {
|
||||
cJSON_AddStringToObject(info, "banner", g_unified_cache.relay_info.banner);
|
||||
|
||||
if (relay_banner && strlen(relay_banner) > 0) {
|
||||
cJSON_AddStringToObject(info, "banner", relay_banner);
|
||||
free((char*)relay_banner);
|
||||
}
|
||||
if (strlen(g_unified_cache.relay_info.icon) > 0) {
|
||||
cJSON_AddStringToObject(info, "icon", g_unified_cache.relay_info.icon);
|
||||
|
||||
if (relay_icon && strlen(relay_icon) > 0) {
|
||||
cJSON_AddStringToObject(info, "icon", relay_icon);
|
||||
free((char*)relay_icon);
|
||||
}
|
||||
if (strlen(g_unified_cache.relay_info.pubkey) > 0) {
|
||||
cJSON_AddStringToObject(info, "pubkey", g_unified_cache.relay_info.pubkey);
|
||||
|
||||
if (relay_pubkey && strlen(relay_pubkey) > 0) {
|
||||
cJSON_AddStringToObject(info, "pubkey", relay_pubkey);
|
||||
free((char*)relay_pubkey);
|
||||
}
|
||||
if (strlen(g_unified_cache.relay_info.contact) > 0) {
|
||||
cJSON_AddStringToObject(info, "contact", g_unified_cache.relay_info.contact);
|
||||
|
||||
if (relay_contact && strlen(relay_contact) > 0) {
|
||||
cJSON_AddStringToObject(info, "contact", relay_contact);
|
||||
free((char*)relay_contact);
|
||||
}
|
||||
|
||||
|
||||
// Add supported NIPs
|
||||
if (g_unified_cache.relay_info.supported_nips) {
|
||||
cJSON_AddItemToObject(info, "supported_nips", cJSON_Duplicate(g_unified_cache.relay_info.supported_nips, 1));
|
||||
if (supported_nips_csv && strlen(supported_nips_csv) > 0) {
|
||||
cJSON* supported_nips = parse_comma_separated_array(supported_nips_csv);
|
||||
if (supported_nips) {
|
||||
cJSON_AddItemToObject(info, "supported_nips", supported_nips);
|
||||
}
|
||||
free((char*)supported_nips_csv);
|
||||
} else {
|
||||
// Default supported NIPs
|
||||
cJSON* supported_nips = cJSON_CreateArray();
|
||||
if (supported_nips) {
|
||||
cJSON_AddItemToArray(supported_nips, cJSON_CreateNumber(1)); // NIP-01: Basic protocol
|
||||
cJSON_AddItemToArray(supported_nips, cJSON_CreateNumber(9)); // NIP-09: Event deletion
|
||||
cJSON_AddItemToArray(supported_nips, cJSON_CreateNumber(11)); // NIP-11: Relay information
|
||||
cJSON_AddItemToArray(supported_nips, cJSON_CreateNumber(13)); // NIP-13: Proof of Work
|
||||
cJSON_AddItemToArray(supported_nips, cJSON_CreateNumber(15)); // NIP-15: EOSE
|
||||
cJSON_AddItemToArray(supported_nips, cJSON_CreateNumber(20)); // NIP-20: Command results
|
||||
cJSON_AddItemToArray(supported_nips, cJSON_CreateNumber(40)); // NIP-40: Expiration Timestamp
|
||||
cJSON_AddItemToArray(supported_nips, cJSON_CreateNumber(42)); // NIP-42: Authentication
|
||||
cJSON_AddItemToObject(info, "supported_nips", supported_nips);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add software information
|
||||
if (strlen(g_unified_cache.relay_info.software) > 0) {
|
||||
cJSON_AddStringToObject(info, "software", g_unified_cache.relay_info.software);
|
||||
if (relay_software && strlen(relay_software) > 0) {
|
||||
cJSON_AddStringToObject(info, "software", relay_software);
|
||||
free((char*)relay_software);
|
||||
} else {
|
||||
cJSON_AddStringToObject(info, "software", "https://git.laantungir.net/laantungir/c-relay.git");
|
||||
}
|
||||
if (strlen(g_unified_cache.relay_info.version) > 0) {
|
||||
cJSON_AddStringToObject(info, "version", g_unified_cache.relay_info.version);
|
||||
|
||||
if (relay_version && strlen(relay_version) > 0) {
|
||||
cJSON_AddStringToObject(info, "version", relay_version);
|
||||
free((char*)relay_version);
|
||||
} else {
|
||||
cJSON_AddStringToObject(info, "version", "0.2.0");
|
||||
}
|
||||
|
||||
|
||||
// Add policies
|
||||
if (strlen(g_unified_cache.relay_info.privacy_policy) > 0) {
|
||||
cJSON_AddStringToObject(info, "privacy_policy", g_unified_cache.relay_info.privacy_policy);
|
||||
if (privacy_policy && strlen(privacy_policy) > 0) {
|
||||
cJSON_AddStringToObject(info, "privacy_policy", privacy_policy);
|
||||
free((char*)privacy_policy);
|
||||
}
|
||||
if (strlen(g_unified_cache.relay_info.terms_of_service) > 0) {
|
||||
cJSON_AddStringToObject(info, "terms_of_service", g_unified_cache.relay_info.terms_of_service);
|
||||
|
||||
if (terms_of_service && strlen(terms_of_service) > 0) {
|
||||
cJSON_AddStringToObject(info, "terms_of_service", terms_of_service);
|
||||
free((char*)terms_of_service);
|
||||
}
|
||||
if (strlen(g_unified_cache.relay_info.posting_policy) > 0) {
|
||||
cJSON_AddStringToObject(info, "posting_policy", g_unified_cache.relay_info.posting_policy);
|
||||
|
||||
if (posting_policy && strlen(posting_policy) > 0) {
|
||||
cJSON_AddStringToObject(info, "posting_policy", posting_policy);
|
||||
free((char*)posting_policy);
|
||||
}
|
||||
|
||||
|
||||
// Add server limitations
|
||||
if (g_unified_cache.relay_info.limitation) {
|
||||
cJSON_AddItemToObject(info, "limitation", cJSON_Duplicate(g_unified_cache.relay_info.limitation, 1));
|
||||
cJSON* limitation = cJSON_CreateObject();
|
||||
if (limitation) {
|
||||
cJSON_AddNumberToObject(limitation, "max_message_length", max_message_length);
|
||||
cJSON_AddNumberToObject(limitation, "max_subscriptions", max_subscriptions_per_client);
|
||||
cJSON_AddNumberToObject(limitation, "max_limit", max_limit);
|
||||
cJSON_AddNumberToObject(limitation, "max_subid_length", SUBSCRIPTION_ID_MAX_LENGTH);
|
||||
cJSON_AddNumberToObject(limitation, "max_event_tags", max_event_tags);
|
||||
cJSON_AddNumberToObject(limitation, "max_content_length", max_content_length);
|
||||
cJSON_AddNumberToObject(limitation, "min_pow_difficulty", min_pow_difficulty);
|
||||
cJSON_AddBoolToObject(limitation, "auth_required", admin_enabled ? cJSON_True : cJSON_False);
|
||||
cJSON_AddBoolToObject(limitation, "payment_required", cJSON_False);
|
||||
cJSON_AddBoolToObject(limitation, "restricted_writes", cJSON_False);
|
||||
cJSON_AddNumberToObject(limitation, "created_at_lower_limit", 0);
|
||||
cJSON_AddNumberToObject(limitation, "created_at_upper_limit", 2147483647);
|
||||
cJSON_AddNumberToObject(limitation, "default_limit", default_limit);
|
||||
cJSON_AddItemToObject(info, "limitation", limitation);
|
||||
}
|
||||
|
||||
// Add retention policies if configured
|
||||
if (g_unified_cache.relay_info.retention && cJSON_GetArraySize(g_unified_cache.relay_info.retention) > 0) {
|
||||
cJSON_AddItemToObject(info, "retention", cJSON_Duplicate(g_unified_cache.relay_info.retention, 1));
|
||||
|
||||
// Add retention policies (empty array for now)
|
||||
cJSON* retention = cJSON_CreateArray();
|
||||
if (retention) {
|
||||
cJSON_AddItemToObject(info, "retention", retention);
|
||||
}
|
||||
|
||||
|
||||
// Add geographical and language information
|
||||
if (g_unified_cache.relay_info.relay_countries) {
|
||||
cJSON_AddItemToObject(info, "relay_countries", cJSON_Duplicate(g_unified_cache.relay_info.relay_countries, 1));
|
||||
if (relay_countries_csv && strlen(relay_countries_csv) > 0) {
|
||||
cJSON* relay_countries = parse_comma_separated_array(relay_countries_csv);
|
||||
if (relay_countries) {
|
||||
cJSON_AddItemToObject(info, "relay_countries", relay_countries);
|
||||
}
|
||||
free((char*)relay_countries_csv);
|
||||
} else {
|
||||
cJSON* relay_countries = cJSON_CreateArray();
|
||||
if (relay_countries) {
|
||||
cJSON_AddItemToArray(relay_countries, cJSON_CreateString("*"));
|
||||
cJSON_AddItemToObject(info, "relay_countries", relay_countries);
|
||||
}
|
||||
}
|
||||
if (g_unified_cache.relay_info.language_tags) {
|
||||
cJSON_AddItemToObject(info, "language_tags", cJSON_Duplicate(g_unified_cache.relay_info.language_tags, 1));
|
||||
|
||||
if (language_tags_csv && strlen(language_tags_csv) > 0) {
|
||||
cJSON* language_tags = parse_comma_separated_array(language_tags_csv);
|
||||
if (language_tags) {
|
||||
cJSON_AddItemToObject(info, "language_tags", language_tags);
|
||||
}
|
||||
free((char*)language_tags_csv);
|
||||
} else {
|
||||
cJSON* language_tags = cJSON_CreateArray();
|
||||
if (language_tags) {
|
||||
cJSON_AddItemToArray(language_tags, cJSON_CreateString("*"));
|
||||
cJSON_AddItemToObject(info, "language_tags", language_tags);
|
||||
}
|
||||
}
|
||||
if (g_unified_cache.relay_info.tags && cJSON_GetArraySize(g_unified_cache.relay_info.tags) > 0) {
|
||||
cJSON_AddItemToObject(info, "tags", cJSON_Duplicate(g_unified_cache.relay_info.tags, 1));
|
||||
|
||||
// Add content tags (empty array)
|
||||
cJSON* tags = cJSON_CreateArray();
|
||||
if (tags) {
|
||||
cJSON_AddItemToObject(info, "tags", tags);
|
||||
}
|
||||
|
||||
|
||||
// Add payment information if configured
|
||||
if (strlen(g_unified_cache.relay_info.payments_url) > 0) {
|
||||
cJSON_AddStringToObject(info, "payments_url", g_unified_cache.relay_info.payments_url);
|
||||
if (payments_url && strlen(payments_url) > 0) {
|
||||
cJSON_AddStringToObject(info, "payments_url", payments_url);
|
||||
free((char*)payments_url);
|
||||
}
|
||||
if (g_unified_cache.relay_info.fees && cJSON_GetObjectItem(g_unified_cache.relay_info.fees, "admission")) {
|
||||
cJSON_AddItemToObject(info, "fees", cJSON_Duplicate(g_unified_cache.relay_info.fees, 1));
|
||||
|
||||
// Add fees (empty object - no payment required by default)
|
||||
cJSON* fees = cJSON_CreateObject();
|
||||
if (fees) {
|
||||
cJSON_AddItemToObject(info, "fees", fees);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
75
src/nip013.c
75
src/nip013.c
@@ -10,63 +10,38 @@
|
||||
#include "config.h"
|
||||
|
||||
|
||||
// NIP-13 PoW configuration structure
|
||||
struct pow_config {
|
||||
int enabled; // 0 = disabled, 1 = enabled
|
||||
int min_pow_difficulty; // Minimum required difficulty (0 = no requirement)
|
||||
int validation_flags; // Bitflags for validation options
|
||||
int require_nonce_tag; // 1 = require nonce tag presence
|
||||
int reject_lower_targets; // 1 = reject if committed < actual difficulty
|
||||
int strict_format; // 1 = enforce strict nonce tag format
|
||||
int anti_spam_mode; // 1 = full anti-spam validation
|
||||
};
|
||||
// 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() {
|
||||
|
||||
// Get all config values first (without holding mutex to avoid deadlock)
|
||||
int pow_enabled = get_config_bool("pow_enabled", 1);
|
||||
int pow_min_difficulty = get_config_int("pow_min_difficulty", 0);
|
||||
const char* pow_mode = get_config_value("pow_mode");
|
||||
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
|
||||
// Load PoW settings from configuration system
|
||||
g_unified_cache.pow_config.enabled = pow_enabled;
|
||||
g_unified_cache.pow_config.min_pow_difficulty = pow_min_difficulty;
|
||||
|
||||
// Configure PoW mode
|
||||
if (pow_mode) {
|
||||
if (strcmp(pow_mode, "strict") == 0) {
|
||||
g_unified_cache.pow_config.validation_flags = NOSTR_POW_VALIDATE_ANTI_SPAM | NOSTR_POW_STRICT_FORMAT;
|
||||
g_unified_cache.pow_config.require_nonce_tag = 1;
|
||||
g_unified_cache.pow_config.reject_lower_targets = 1;
|
||||
g_unified_cache.pow_config.strict_format = 1;
|
||||
g_unified_cache.pow_config.anti_spam_mode = 1;
|
||||
} else if (strcmp(pow_mode, "full") == 0) {
|
||||
g_unified_cache.pow_config.validation_flags = NOSTR_POW_VALIDATE_FULL;
|
||||
g_unified_cache.pow_config.require_nonce_tag = 1;
|
||||
} else if (strcmp(pow_mode, "basic") == 0) {
|
||||
g_unified_cache.pow_config.validation_flags = NOSTR_POW_VALIDATE_BASIC;
|
||||
} else if (strcmp(pow_mode, "disabled") == 0) {
|
||||
g_unified_cache.pow_config.enabled = 0;
|
||||
}
|
||||
free((char*)pow_mode); // Free dynamically allocated string
|
||||
} else {
|
||||
// Default to basic mode
|
||||
g_unified_cache.pow_config.validation_flags = NOSTR_POW_VALIDATE_BASIC;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
// 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) {
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
int enabled = g_unified_cache.pow_config.enabled;
|
||||
int min_pow_difficulty = g_unified_cache.pow_config.min_pow_difficulty;
|
||||
int validation_flags = g_unified_cache.pow_config.validation_flags;
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
// 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
|
||||
|
||||
@@ -360,11 +360,9 @@ int nostr_validate_unified_request(const char* json_string, size_t json_length)
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 12. NIP-13 Proof of Work validation
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
int pow_enabled = g_unified_cache.pow_config.enabled;
|
||||
int pow_min_difficulty = g_unified_cache.pow_config.min_pow_difficulty;
|
||||
int pow_validation_flags = g_unified_cache.pow_config.validation_flags;
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
int pow_enabled = get_config_bool("pow_enabled", 0);
|
||||
int pow_min_difficulty = get_config_int("pow_min_difficulty", 0);
|
||||
int pow_validation_flags = get_config_int("pow_validation_flags", 1);
|
||||
|
||||
if (pow_enabled && pow_min_difficulty > 0) {
|
||||
nostr_pow_result_t pow_result;
|
||||
@@ -505,11 +503,10 @@ void nostr_request_result_free_file_data(nostr_request_result_t *result) {
|
||||
|
||||
|
||||
/**
|
||||
* Force cache refresh - use unified cache system
|
||||
* Force cache refresh - cache no longer exists, function kept for compatibility
|
||||
*/
|
||||
void nostr_request_validator_force_cache_refresh(void) {
|
||||
// Use unified cache refresh from config.c
|
||||
force_config_cache_refresh();
|
||||
// Cache no longer exists - direct database queries are used
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -180,34 +180,6 @@ BEGIN\n\
|
||||
UPDATE config SET updated_at = strftime('%s', 'now') WHERE key = NEW.key;\n\
|
||||
END;\n\
|
||||
\n\
|
||||
-- Insert default configuration values\n\
|
||||
INSERT INTO config (key, value, data_type, description, category, requires_restart) VALUES\n\
|
||||
('relay_description', 'A C Nostr Relay', 'string', 'Relay description', 'general', 0),\n\
|
||||
('relay_contact', '', 'string', 'Relay contact information', 'general', 0),\n\
|
||||
('relay_software', 'https://github.com/laanwj/c-relay', 'string', 'Relay software URL', 'general', 0),\n\
|
||||
('relay_version', '1.0.0', 'string', 'Relay version', 'general', 0),\n\
|
||||
('relay_port', '8888', 'integer', 'Relay port number', 'network', 1),\n\
|
||||
('max_connections', '1000', 'integer', 'Maximum concurrent connections', 'network', 1),\n\
|
||||
('auth_enabled', 'false', 'boolean', 'Enable NIP-42 authentication', 'auth', 0),\n\
|
||||
('nip42_auth_required_events', 'false', 'boolean', 'Require auth for event publishing', 'auth', 0),\n\
|
||||
('nip42_auth_required_subscriptions', 'false', 'boolean', 'Require auth for subscriptions', 'auth', 0),\n\
|
||||
('nip42_auth_required_kinds', '[]', 'json', 'Event kinds requiring authentication', 'auth', 0),\n\
|
||||
('nip42_challenge_expiration', '600', 'integer', 'Auth challenge expiration seconds', 'auth', 0),\n\
|
||||
('pow_min_difficulty', '0', 'integer', 'Minimum proof-of-work difficulty', 'validation', 0),\n\
|
||||
('pow_mode', 'optional', 'string', 'Proof-of-work mode', 'validation', 0),\n\
|
||||
('nip40_expiration_enabled', 'true', 'boolean', 'Enable event expiration', 'validation', 0),\n\
|
||||
('nip40_expiration_strict', 'false', 'boolean', 'Strict expiration mode', 'validation', 0),\n\
|
||||
('nip40_expiration_filter', 'true', 'boolean', 'Filter expired events in queries', 'validation', 0),\n\
|
||||
('nip40_expiration_grace_period', '60', 'integer', 'Expiration grace period seconds', 'validation', 0),\n\
|
||||
('max_subscriptions_per_client', '25', 'integer', 'Maximum subscriptions per client', 'limits', 0),\n\
|
||||
('max_total_subscriptions', '1000', 'integer', 'Maximum total subscriptions', 'limits', 0),\n\
|
||||
('max_filters_per_subscription', '10', 'integer', 'Maximum filters per subscription', 'limits', 0),\n\
|
||||
('max_event_tags', '2000', 'integer', 'Maximum tags per event', 'limits', 0),\n\
|
||||
('max_content_length', '100000', 'integer', 'Maximum event content length', 'limits', 0),\n\
|
||||
('max_message_length', '131072', 'integer', 'Maximum WebSocket message length', 'limits', 0),\n\
|
||||
('default_limit', '100', 'integer', 'Default query limit', 'limits', 0),\n\
|
||||
('max_limit', '5000', 'integer', 'Maximum query limit', 'limits', 0);\n\
|
||||
\n\
|
||||
-- Persistent Subscriptions Logging Tables (Phase 2)\n\
|
||||
-- Optional database logging for subscription analytics and debugging\n\
|
||||
\n\
|
||||
|
||||
@@ -28,8 +28,8 @@ int validate_search_term(const char* search_term, char* error_message, size_t er
|
||||
// Global database variable
|
||||
extern sqlite3* g_db;
|
||||
|
||||
// Global unified cache
|
||||
extern unified_config_cache_t g_unified_cache;
|
||||
// Configuration functions from config.c
|
||||
extern int get_config_bool(const char* key, int default_value);
|
||||
|
||||
// Global subscription manager
|
||||
extern subscription_manager_t g_subscription_manager;
|
||||
@@ -534,10 +534,8 @@ int broadcast_event_to_subscriptions(cJSON* event) {
|
||||
}
|
||||
|
||||
// Check if event is expired and should not be broadcast (NIP-40)
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
int expiration_enabled = g_unified_cache.expiration_config.enabled;
|
||||
int filter_responses = g_unified_cache.expiration_config.filter_responses;
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
int expiration_enabled = get_config_bool("expiration_enabled", 1);
|
||||
int filter_responses = get_config_bool("expiration_filter", 1);
|
||||
|
||||
if (expiration_enabled && filter_responses) {
|
||||
time_t current_time = time(NULL);
|
||||
|
||||
@@ -93,8 +93,8 @@ int validate_filter_array(cJSON* filters, char* error_message, size_t error_size
|
||||
// Forward declarations for NOTICE message support
|
||||
void send_notice_message(struct lws* wsi, const char* message);
|
||||
|
||||
// Forward declarations for unified cache access
|
||||
extern unified_config_cache_t g_unified_cache;
|
||||
// Configuration functions from config.c
|
||||
extern int get_config_bool(const char* key, int default_value);
|
||||
|
||||
// Forward declarations for global state
|
||||
extern sqlite3* g_db;
|
||||
@@ -453,8 +453,8 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
||||
}
|
||||
|
||||
if (is_protected_event) {
|
||||
// Check if protected events are enabled using unified cache
|
||||
int protected_events_enabled = g_unified_cache.nip70_protected_events_enabled;
|
||||
// Check if protected events are enabled using config
|
||||
int protected_events_enabled = get_config_bool("nip70_protected_events_enabled", 0);
|
||||
|
||||
if (!protected_events_enabled) {
|
||||
// Protected events not supported
|
||||
|
||||
Reference in New Issue
Block a user