implemented command line
This commit is contained in:
80
Makefile
Normal file
80
Makefile
Normal file
@@ -0,0 +1,80 @@
|
||||
# Event Miner Makefile
|
||||
# Static build with nostr_core_lib dependency
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -std=c99 -O2 -static
|
||||
INCLUDES = -Inostr_core_lib/nostr_core
|
||||
LIBS = -lm -lpthread
|
||||
NOSTR_LIB = nostr_core_lib/libnostr_core.a
|
||||
|
||||
# Default target
|
||||
all: event_miner
|
||||
|
||||
# Main target - build event_miner
|
||||
event_miner: event_miner.c $(NOSTR_LIB)
|
||||
@echo "Building event_miner..."
|
||||
$(CC) $(CFLAGS) $(INCLUDES) event_miner.c $(NOSTR_LIB) $(LIBS) -o event_miner
|
||||
@echo "✓ event_miner built successfully"
|
||||
|
||||
# Build nostr_core_lib dependency
|
||||
$(NOSTR_LIB):
|
||||
@echo "Building nostr_core_lib dependency..."
|
||||
cd nostr_core_lib && make
|
||||
@echo "✓ nostr_core_lib built successfully"
|
||||
|
||||
# Clean target
|
||||
clean:
|
||||
@echo "Cleaning build artifacts..."
|
||||
rm -f event_miner
|
||||
cd nostr_core_lib && make clean
|
||||
|
||||
# Clean only event_miner (keep nostr_core_lib)
|
||||
clean-miner:
|
||||
@echo "Cleaning event_miner..."
|
||||
rm -f event_miner
|
||||
|
||||
# Test target (basic compilation test)
|
||||
test: event_miner
|
||||
@echo "Testing event_miner compilation..."
|
||||
./event_miner --help
|
||||
@echo "✓ Basic test passed"
|
||||
|
||||
# Install target (optional - install to /usr/local/bin)
|
||||
install: event_miner
|
||||
@echo "Installing event_miner to /usr/local/bin..."
|
||||
sudo cp event_miner /usr/local/bin/
|
||||
@echo "✓ event_miner installed"
|
||||
|
||||
# Uninstall target
|
||||
uninstall:
|
||||
@echo "Removing event_miner from /usr/local/bin..."
|
||||
sudo rm -f /usr/local/bin/event_miner
|
||||
@echo "✓ event_miner uninstalled"
|
||||
|
||||
# Force rebuild
|
||||
rebuild: clean all
|
||||
|
||||
# Help
|
||||
help:
|
||||
@echo "Event Miner Build System"
|
||||
@echo "========================"
|
||||
@echo ""
|
||||
@echo "Available targets:"
|
||||
@echo " all - Build event_miner (default)"
|
||||
@echo " event_miner - Build event_miner binary"
|
||||
@echo " clean - Clean all build artifacts"
|
||||
@echo " clean-miner - Clean only event_miner binary"
|
||||
@echo " test - Build and run basic test"
|
||||
@echo " install - Install to /usr/local/bin (requires sudo)"
|
||||
@echo " uninstall - Remove from /usr/local/bin (requires sudo)"
|
||||
@echo " rebuild - Clean and rebuild everything"
|
||||
@echo " help - Show this help"
|
||||
@echo ""
|
||||
@echo "Usage examples:"
|
||||
@echo " make # Build event_miner"
|
||||
@echo " make clean && make # Clean rebuild"
|
||||
@echo " make test # Build and test"
|
||||
@echo ""
|
||||
@echo "The binary will be statically linked with no external dependencies."
|
||||
|
||||
.PHONY: all clean clean-miner test install uninstall rebuild help
|
||||
158
README.md
Normal file
158
README.md
Normal file
@@ -0,0 +1,158 @@
|
||||
# Event Miner
|
||||
|
||||
A multithreaded command-line tool for adding NIP-13 Proof-of-Work to Nostr events using the nostr_core_lib.
|
||||
|
||||
## Features
|
||||
|
||||
- **Multithreaded mining** - Use multiple CPU cores for faster mining
|
||||
- **Static linking** - No external dependencies, self-contained binary
|
||||
- **Flexible input** - Read events from stdin or files
|
||||
- **Timeout support** - Optional mining timeout
|
||||
- **Standards compliant** - Uses NIP-13 proof-of-work specification
|
||||
|
||||
## Building
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- GCC compiler
|
||||
- Make
|
||||
- Git (for submodules)
|
||||
|
||||
### Build Steps
|
||||
|
||||
```bash
|
||||
# Clone the repository (if not already done)
|
||||
git clone <repository-url>
|
||||
cd event_miner
|
||||
|
||||
# Initialize and update the nostr_core_lib submodule (if needed)
|
||||
git submodule update --init --recursive
|
||||
|
||||
# Build the project
|
||||
make
|
||||
|
||||
# The static binary will be created as ./event_miner
|
||||
```
|
||||
|
||||
### Build Options
|
||||
|
||||
```bash
|
||||
make # Build event_miner
|
||||
make clean # Clean all build artifacts
|
||||
make clean-miner # Clean only event_miner binary
|
||||
make test # Build and run basic test
|
||||
make help # Show build help
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Syntax
|
||||
|
||||
```bash
|
||||
./event_miner -pow <difficulty> -nsec <private_key> -threads <count> [options]
|
||||
```
|
||||
|
||||
### Required Arguments
|
||||
|
||||
- `-pow <difficulty>` - Number of leading zero bits for proof-of-work (e.g., 4 for difficulty 4)
|
||||
- `-nsec <private_key>` - Private key in hex or nsec bech32 format
|
||||
- `-threads <count>` - Number of mining threads to use
|
||||
|
||||
### Optional Arguments
|
||||
|
||||
- `-e <filename>` - Read event from file (default: stdin)
|
||||
- `--timeout_min <minutes>` - Mining timeout in minutes (default: no timeout)
|
||||
- `-h, --help` - Show help message
|
||||
|
||||
### Examples
|
||||
|
||||
#### Using stdin
|
||||
```bash
|
||||
echo '{"kind":1,"content":"Hello Nostr!","tags":[],"created_at":1723666800,"pubkey":"..."}' | \
|
||||
./event_miner -pow 4 -nsec nsec1abc123... -threads 8
|
||||
```
|
||||
|
||||
#### Using file input
|
||||
```bash
|
||||
./event_miner -pow 4 -nsec 1234567890abcdef... -threads 8 -e event.json
|
||||
```
|
||||
|
||||
#### With timeout
|
||||
```bash
|
||||
./event_miner -pow 6 -nsec nsec1abc123... -threads 4 -e event.json --timeout_min 10
|
||||
```
|
||||
|
||||
### Input Format
|
||||
|
||||
The input event must be a valid JSON object with the standard Nostr event structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": 1,
|
||||
"content": "Hello, Nostr!",
|
||||
"tags": [],
|
||||
"created_at": 1723666800,
|
||||
"pubkey": "..."
|
||||
}
|
||||
```
|
||||
|
||||
### Output Format
|
||||
|
||||
The mined event will be output to stdout with the proof-of-work nonce added:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": 1,
|
||||
"content": "Hello, Nostr!",
|
||||
"pubkey": "...",
|
||||
"id": "...",
|
||||
"sig": "...",
|
||||
"tags": [["nonce", "12345", "4"]],
|
||||
"created_at": 1755197090
|
||||
}
|
||||
```
|
||||
|
||||
The `nonce` tag contains:
|
||||
- The nonce value that produces the required difficulty
|
||||
- The target difficulty level
|
||||
- Additional PoW-related data per NIP-13
|
||||
|
||||
## Private Key Formats
|
||||
|
||||
The tool accepts private keys in two formats:
|
||||
|
||||
1. **Hex format** (64 characters): `1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef`
|
||||
2. **Bech32 nsec format**: `nsec1abc123def456...`
|
||||
|
||||
## Performance Tips
|
||||
|
||||
- Use more threads on multi-core systems for faster mining
|
||||
- Higher difficulty levels take exponentially more time
|
||||
- Consider using timeout for very high difficulty levels
|
||||
- The tool automatically manages thread coordination and early termination
|
||||
|
||||
## Architecture
|
||||
|
||||
- **Single-file implementation** - All code in `event_miner.c`
|
||||
- **Static linking** - Links with `nostr_core_lib/libnostr_core.a`
|
||||
- **Thread-safe** - Uses pthread mutexes and condition variables
|
||||
- **Memory efficient** - Proper cleanup and error handling
|
||||
- **Standards compliant** - Implements NIP-13 proof-of-work specification
|
||||
|
||||
## Dependencies
|
||||
|
||||
The project uses the nostr_core_lib submodule which includes:
|
||||
- secp256k1 for cryptographic operations
|
||||
- cJSON for JSON handling
|
||||
- OpenSSL for additional crypto functions
|
||||
|
||||
All dependencies are statically linked into the final binary.
|
||||
|
||||
## Exit Codes
|
||||
|
||||
- `0` - Success (mined event output to stdout)
|
||||
- `1` - Error (invalid arguments, file not found, timeout, etc.)
|
||||
|
||||
## License
|
||||
|
||||
This project uses the same license as the nostr_core_lib dependency.
|
||||
BIN
event_miner
Executable file
BIN
event_miner
Executable file
Binary file not shown.
430
event_miner.c
Normal file
430
event_miner.c
Normal file
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
* Event Miner - Nostr Proof-of-Work Mining Tool
|
||||
*
|
||||
* A multithreaded command-line tool for adding NIP-13 Proof-of-Work to Nostr events.
|
||||
* Uses the nostr_core_lib for cryptographic operations and event handling.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE // For strdup
|
||||
#define _POSIX_C_SOURCE 200112L // For usleep
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include "nostr_core_lib/nostr_core/nostr_core.h"
|
||||
#include "nostr_core_lib/nostr_core/cJSON.h"
|
||||
|
||||
// Constants
|
||||
#define MAX_EVENT_SIZE 1048576 // 1MB max event size
|
||||
#define DEFAULT_THREADS 4
|
||||
#define DEFAULT_POW 2
|
||||
|
||||
// Data structures
|
||||
typedef struct {
|
||||
cJSON* event;
|
||||
unsigned char private_key[32];
|
||||
int target_difficulty;
|
||||
volatile int found;
|
||||
cJSON* result_event;
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
time_t start_time;
|
||||
int timeout_seconds;
|
||||
int thread_count;
|
||||
} mining_context_t;
|
||||
|
||||
typedef struct {
|
||||
int pow;
|
||||
char* nsec;
|
||||
int threads;
|
||||
char* event_file;
|
||||
int timeout_min;
|
||||
int help;
|
||||
} args_t;
|
||||
|
||||
// Function declarations
|
||||
static void usage(const char* prog_name);
|
||||
static int parse_arguments(int argc, char* argv[], args_t* args);
|
||||
static char* read_event_json(const char* filename);
|
||||
static char* read_stdin_json(void);
|
||||
static void* miner_thread(void* arg);
|
||||
static int mine_event(mining_context_t* ctx);
|
||||
static void cleanup_context(mining_context_t* ctx);
|
||||
|
||||
// Usage information
|
||||
static void usage(const char* prog_name) {
|
||||
fprintf(stderr, "Usage: %s -pow <difficulty> -nsec <private_key> -threads <count> [options]\n\n", prog_name);
|
||||
fprintf(stderr, "Required arguments:\n");
|
||||
fprintf(stderr, " -pow <difficulty> Number of leading zero bits for proof-of-work\n");
|
||||
fprintf(stderr, " -nsec <private_key> Private key in hex or nsec bech32 format\n");
|
||||
fprintf(stderr, " -threads <count> Number of mining threads to use\n\n");
|
||||
fprintf(stderr, "Optional arguments:\n");
|
||||
fprintf(stderr, " -e <filename> Read event from file (default: stdin)\n");
|
||||
fprintf(stderr, " --timeout_min <min> Timeout in minutes (default: no timeout)\n");
|
||||
fprintf(stderr, " -h, --help Show this help message\n\n");
|
||||
fprintf(stderr, "Examples:\n");
|
||||
fprintf(stderr, " echo '{\"kind\":1,...}' | %s -pow 4 -nsec nsec1... -threads 8\n", prog_name);
|
||||
fprintf(stderr, " %s -pow 4 -nsec abc123... -threads 8 -e event.json --timeout_min 10\n", prog_name);
|
||||
}
|
||||
|
||||
// Parse command line arguments
|
||||
static int parse_arguments(int argc, char* argv[], args_t* args) {
|
||||
// Initialize args structure
|
||||
memset(args, 0, sizeof(args_t));
|
||||
args->pow = -1; // Indicates not set
|
||||
args->threads = -1; // Indicates not set
|
||||
|
||||
// Simple manual parsing to avoid getopt complexities with multi-char options
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-pow") == 0 && i + 1 < argc) {
|
||||
args->pow = atoi(argv[i + 1]);
|
||||
if (args->pow <= 0) {
|
||||
fprintf(stderr, "Error: pow must be a positive integer\n");
|
||||
return -1;
|
||||
}
|
||||
i++; // Skip the next argument
|
||||
} else if (strcmp(argv[i], "-nsec") == 0 && i + 1 < argc) {
|
||||
if (args->nsec) free(args->nsec);
|
||||
args->nsec = strdup(argv[i + 1]);
|
||||
if (!args->nsec) {
|
||||
fprintf(stderr, "Error: memory allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
i++; // Skip the next argument
|
||||
} else if (strcmp(argv[i], "-threads") == 0 && i + 1 < argc) {
|
||||
args->threads = atoi(argv[i + 1]);
|
||||
if (args->threads <= 0) {
|
||||
fprintf(stderr, "Error: threads must be a positive integer\n");
|
||||
return -1;
|
||||
}
|
||||
i++; // Skip the next argument
|
||||
} else if (strcmp(argv[i], "-e") == 0 && i + 1 < argc) {
|
||||
args->event_file = strdup(argv[i + 1]);
|
||||
if (!args->event_file) {
|
||||
fprintf(stderr, "Error: memory allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
i++; // Skip the next argument
|
||||
} else if (strcmp(argv[i], "--timeout_min") == 0 && i + 1 < argc) {
|
||||
args->timeout_min = atoi(argv[i + 1]);
|
||||
if (args->timeout_min <= 0) {
|
||||
fprintf(stderr, "Error: timeout_min must be a positive integer\n");
|
||||
return -1;
|
||||
}
|
||||
i++; // Skip the next argument
|
||||
} else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
|
||||
args->help = 1;
|
||||
return 0;
|
||||
} else if (argv[i][0] == '-') {
|
||||
fprintf(stderr, "Error: Unknown option '%s'\n", argv[i]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Check required arguments
|
||||
if (args->pow == -1 || !args->nsec || args->threads == -1) {
|
||||
fprintf(stderr, "Error: Missing required arguments\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read event JSON from file
|
||||
static char* read_event_json(const char* filename) {
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (!file) {
|
||||
fprintf(stderr, "Error: Cannot open file '%s': %s\n", filename, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get file size
|
||||
fseek(file, 0, SEEK_END);
|
||||
long file_size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
if (file_size > MAX_EVENT_SIZE) {
|
||||
fprintf(stderr, "Error: Event file too large (max %d bytes)\n", MAX_EVENT_SIZE);
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Allocate buffer and read file
|
||||
char* buffer = malloc(file_size + 1);
|
||||
if (!buffer) {
|
||||
fprintf(stderr, "Error: Memory allocation failed\n");
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t bytes_read = fread(buffer, 1, file_size, file);
|
||||
buffer[bytes_read] = '\0';
|
||||
|
||||
fclose(file);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Read event JSON from stdin
|
||||
static char* read_stdin_json(void) {
|
||||
char* buffer = malloc(MAX_EVENT_SIZE);
|
||||
if (!buffer) {
|
||||
fprintf(stderr, "Error: Memory allocation failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t total_read = 0;
|
||||
char chunk[4096];
|
||||
|
||||
while (fgets(chunk, sizeof(chunk), stdin)) {
|
||||
size_t chunk_len = strlen(chunk);
|
||||
|
||||
if (total_read + chunk_len >= MAX_EVENT_SIZE - 1) {
|
||||
fprintf(stderr, "Error: Input too large (max %d bytes)\n", MAX_EVENT_SIZE);
|
||||
free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(buffer + total_read, chunk);
|
||||
total_read += chunk_len;
|
||||
}
|
||||
|
||||
buffer[total_read] = '\0';
|
||||
|
||||
if (total_read == 0) {
|
||||
fprintf(stderr, "Error: No input received\n");
|
||||
free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Mining thread function
|
||||
static void* miner_thread(void* arg) {
|
||||
mining_context_t* ctx = (mining_context_t*)arg;
|
||||
|
||||
// Create a copy of the event for this thread
|
||||
char* event_str = cJSON_Print(ctx->event);
|
||||
if (!event_str) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON* local_event = cJSON_Parse(event_str);
|
||||
free(event_str);
|
||||
|
||||
if (!local_event) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Mine until solution found or timeout
|
||||
while (!ctx->found) {
|
||||
// Check timeout
|
||||
if (ctx->timeout_seconds > 0) {
|
||||
time_t current_time = time(NULL);
|
||||
if (current_time - ctx->start_time >= ctx->timeout_seconds) {
|
||||
pthread_mutex_lock(&ctx->mutex);
|
||||
if (!ctx->found) {
|
||||
ctx->found = -1; // Timeout flag
|
||||
}
|
||||
pthread_cond_broadcast(&ctx->cond);
|
||||
pthread_mutex_unlock(&ctx->mutex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt mining
|
||||
int result = nostr_add_proof_of_work(local_event, ctx->private_key,
|
||||
ctx->target_difficulty, NULL, NULL);
|
||||
|
||||
if (result == NOSTR_SUCCESS) {
|
||||
pthread_mutex_lock(&ctx->mutex);
|
||||
if (!ctx->found) {
|
||||
ctx->found = 1; // Success flag
|
||||
ctx->result_event = cJSON_Duplicate(local_event, 1);
|
||||
pthread_cond_broadcast(&ctx->cond);
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->mutex);
|
||||
break;
|
||||
}
|
||||
|
||||
// Small delay to prevent CPU overuse
|
||||
usleep(1000); // 1ms
|
||||
}
|
||||
|
||||
cJSON_Delete(local_event);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Main mining function
|
||||
static int mine_event(mining_context_t* ctx) {
|
||||
// Initialize synchronization objects
|
||||
if (pthread_mutex_init(&ctx->mutex, NULL) != 0) {
|
||||
fprintf(stderr, "Error: Failed to initialize mutex\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pthread_cond_init(&ctx->cond, NULL) != 0) {
|
||||
fprintf(stderr, "Error: Failed to initialize condition variable\n");
|
||||
pthread_mutex_destroy(&ctx->mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create worker threads
|
||||
pthread_t* threads = malloc(ctx->thread_count * sizeof(pthread_t));
|
||||
if (!threads) {
|
||||
fprintf(stderr, "Error: Memory allocation failed\n");
|
||||
pthread_mutex_destroy(&ctx->mutex);
|
||||
pthread_cond_destroy(&ctx->cond);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->start_time = time(NULL);
|
||||
|
||||
// Start threads
|
||||
for (int i = 0; i < ctx->thread_count; i++) {
|
||||
if (pthread_create(&threads[i], NULL, miner_thread, ctx) != 0) {
|
||||
fprintf(stderr, "Error: Failed to create thread %d\n", i);
|
||||
// Clean up already created threads
|
||||
ctx->found = -2; // Error flag
|
||||
for (int j = 0; j < i; j++) {
|
||||
pthread_join(threads[j], NULL);
|
||||
}
|
||||
free(threads);
|
||||
pthread_mutex_destroy(&ctx->mutex);
|
||||
pthread_cond_destroy(&ctx->cond);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for solution or timeout
|
||||
pthread_mutex_lock(&ctx->mutex);
|
||||
while (ctx->found == 0) {
|
||||
pthread_cond_wait(&ctx->cond, &ctx->mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->mutex);
|
||||
|
||||
// Wait for all threads to finish
|
||||
for (int i = 0; i < ctx->thread_count; i++) {
|
||||
pthread_join(threads[i], NULL);
|
||||
}
|
||||
|
||||
free(threads);
|
||||
pthread_mutex_destroy(&ctx->mutex);
|
||||
pthread_cond_destroy(&ctx->cond);
|
||||
|
||||
return ctx->found;
|
||||
}
|
||||
|
||||
// Cleanup context
|
||||
static void cleanup_context(mining_context_t* ctx) {
|
||||
if (ctx->event) {
|
||||
cJSON_Delete(ctx->event);
|
||||
ctx->event = NULL;
|
||||
}
|
||||
if (ctx->result_event) {
|
||||
cJSON_Delete(ctx->result_event);
|
||||
ctx->result_event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Main function
|
||||
int main(int argc, char* argv[]) {
|
||||
args_t args;
|
||||
mining_context_t ctx;
|
||||
int exit_code = 0;
|
||||
|
||||
// Initialize context
|
||||
memset(&ctx, 0, sizeof(mining_context_t));
|
||||
|
||||
// Parse arguments
|
||||
if (parse_arguments(argc, argv, &args) != 0) {
|
||||
usage(argv[0]);
|
||||
exit_code = 1;
|
||||
goto cleanup_args;
|
||||
}
|
||||
|
||||
if (args.help) {
|
||||
usage(argv[0]);
|
||||
goto cleanup_args;
|
||||
}
|
||||
|
||||
// Initialize nostr library
|
||||
if (nostr_init() != NOSTR_SUCCESS) {
|
||||
fprintf(stderr, "Error: Failed to initialize nostr_core library\n");
|
||||
exit_code = 1;
|
||||
goto cleanup_args;
|
||||
}
|
||||
|
||||
// Decode private key
|
||||
if (nostr_decode_nsec(args.nsec, ctx.private_key) != NOSTR_SUCCESS) {
|
||||
fprintf(stderr, "Error: Invalid private key format\n");
|
||||
exit_code = 1;
|
||||
goto cleanup_nostr;
|
||||
}
|
||||
|
||||
// Read event JSON
|
||||
char* event_json = NULL;
|
||||
if (args.event_file) {
|
||||
event_json = read_event_json(args.event_file);
|
||||
} else {
|
||||
event_json = read_stdin_json();
|
||||
}
|
||||
|
||||
if (!event_json) {
|
||||
exit_code = 1;
|
||||
goto cleanup_nostr;
|
||||
}
|
||||
|
||||
// Parse JSON event
|
||||
ctx.event = cJSON_Parse(event_json);
|
||||
free(event_json);
|
||||
|
||||
if (!ctx.event) {
|
||||
fprintf(stderr, "Error: Invalid JSON event format\n");
|
||||
exit_code = 1;
|
||||
goto cleanup_nostr;
|
||||
}
|
||||
|
||||
// Set mining parameters
|
||||
ctx.target_difficulty = args.pow;
|
||||
ctx.thread_count = args.threads;
|
||||
ctx.timeout_seconds = args.timeout_min > 0 ? args.timeout_min * 60 : 0;
|
||||
|
||||
// Start mining
|
||||
int mining_result = mine_event(&ctx);
|
||||
|
||||
if (mining_result == 1 && ctx.result_event) {
|
||||
// Success - output mined event
|
||||
char* output_json = cJSON_Print(ctx.result_event);
|
||||
if (output_json) {
|
||||
printf("%s\n", output_json);
|
||||
free(output_json);
|
||||
} else {
|
||||
fprintf(stderr, "Error: Failed to serialize result event\n");
|
||||
exit_code = 1;
|
||||
}
|
||||
} else if (mining_result == -1) {
|
||||
fprintf(stderr, "Error: Mining timeout reached\n");
|
||||
exit_code = 1;
|
||||
} else {
|
||||
fprintf(stderr, "Error: Mining failed\n");
|
||||
exit_code = 1;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
cleanup_context(&ctx);
|
||||
|
||||
cleanup_nostr:
|
||||
nostr_cleanup();
|
||||
|
||||
cleanup_args:
|
||||
if (args.nsec) free(args.nsec);
|
||||
if (args.event_file) free(args.event_file);
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
7
real_event.json
Normal file
7
real_event.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"kind": 1,
|
||||
"created_at": 1755197329,
|
||||
"tags": [],
|
||||
"content": "Hello POW!",
|
||||
"pubkey": "4966622dc55b1aeb83a3a6dc395fffa4ba32f03c219e793a8f3eefa33e928894"
|
||||
}
|
||||
18
test_data.txt
Normal file
18
test_data.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
{
|
||||
"nsec": "nsec1gn3mazjfhkyufydg8pl8shrne7wtzzvuhem6j9e3dcczshhwyp5stmt0q8",
|
||||
"npub": "npub1f9nxytw9tvdwhqar5mwrjhll5jar9upuyx08jw508mh6x05j3z2q53qndh",
|
||||
"npubHex": "4966622dc55b1aeb83a3a6dc395fffa4ba32f03c219e793a8f3eefa33e928894",
|
||||
"nsecHex": "44e3be8a49bd89c491a8387e785c73cf9cb1099cbe77a917316e30285eee2069"
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
"kind": 1,
|
||||
"created_at": 1755197329,
|
||||
"tags": [],
|
||||
"content": "",
|
||||
"pubkey": "4966622dc55b1aeb83a3a6dc395fffa4ba32f03c219e793a8f3eefa33e928894",
|
||||
"id": "ab044d1714a14e442db7bede4238b6060bfb28e6e5076236a453742ba856ebf6",
|
||||
"sig": "99ff2eab1231d689e776d6714d18facdcbe2aacb993316a6b9bcb2f8f08a91307811f40350aeff4aafc3f220f744c977ccbb99f2e953ea28b3f2c95458ee3c96"
|
||||
}
|
||||
7
test_event.json
Normal file
7
test_event.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"kind": 1,
|
||||
"content": "Hello, Nostr! Testing proof-of-work mining.",
|
||||
"tags": [],
|
||||
"created_at": 1723666800,
|
||||
"pubkey": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
Reference in New Issue
Block a user