Updated build.sh to automatically commit and push changes after successful build
This commit is contained in:
233
event_miner.c
233
event_miner.c
@@ -17,18 +17,46 @@
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include "nostr_core_lib/nostr_core/nostr_core.h"
|
||||
#include "nostr_core_lib/nostr_core/cJSON.h"
|
||||
#include "nostr_core_lib/cjson/cJSON.h"
|
||||
|
||||
// Constants
|
||||
#define MAX_EVENT_SIZE 1048576 // 1MB max event size
|
||||
#define DEFAULT_THREADS 4
|
||||
#define DEFAULT_POW 2
|
||||
|
||||
// Data structures
|
||||
// Forward declarations for callbacks
|
||||
typedef struct mining_context mining_context_t;
|
||||
|
||||
// Callback function types
|
||||
typedef void (*solution_callback_t)(cJSON* solution, void* user_data);
|
||||
typedef void (*progress_callback_t)(int thread_id, uint64_t attempts, void* user_data);
|
||||
typedef void (*error_callback_t)(int thread_id, int error_code, void* user_data);
|
||||
|
||||
// Main context for control decisions
|
||||
typedef struct {
|
||||
volatile int solution_found;
|
||||
volatile int timeout_reached;
|
||||
cJSON* result_event;
|
||||
pthread_mutex_t result_mutex;
|
||||
} main_context_t;
|
||||
|
||||
// Mining context for workers (keeping legacy fields for now during transition)
|
||||
struct mining_context {
|
||||
cJSON* event;
|
||||
unsigned char private_key[32];
|
||||
int target_difficulty;
|
||||
int thread_id;
|
||||
|
||||
// Callbacks for reporting (no control decisions)
|
||||
solution_callback_t solution_callback;
|
||||
progress_callback_t progress_callback;
|
||||
error_callback_t error_callback;
|
||||
void* user_data;
|
||||
|
||||
// Control flag (only main thread modifies)
|
||||
volatile int should_stop;
|
||||
|
||||
// Legacy fields for compatibility during transition
|
||||
volatile int found;
|
||||
cJSON* result_event;
|
||||
pthread_mutex_t mutex;
|
||||
@@ -36,14 +64,14 @@ typedef struct {
|
||||
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 timeout_sec;
|
||||
int help;
|
||||
} args_t;
|
||||
|
||||
@@ -56,6 +84,11 @@ static void* miner_thread(void* arg);
|
||||
static int mine_event(mining_context_t* ctx);
|
||||
static void cleanup_context(mining_context_t* ctx);
|
||||
|
||||
// Callback implementations
|
||||
static void solution_found_callback(cJSON* solution, void* user_data);
|
||||
static void progress_report_callback(int thread_id, uint64_t attempts, void* user_data);
|
||||
static void error_report_callback(int thread_id, int error_code, void* user_data);
|
||||
|
||||
// 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);
|
||||
@@ -65,11 +98,11 @@ static void usage(const char* prog_name) {
|
||||
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, " --timeout_sec <sec> Timeout in seconds (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);
|
||||
fprintf(stderr, " %s -pow 4 -nsec abc123... -threads 8 -e event.json --timeout_sec 60\n", prog_name);
|
||||
}
|
||||
|
||||
// Parse command line arguments
|
||||
@@ -110,10 +143,10 @@ static int parse_arguments(int argc, char* argv[], args_t* args) {
|
||||
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");
|
||||
} else if (strcmp(argv[i], "--timeout_sec") == 0 && i + 1 < argc) {
|
||||
args->timeout_sec = atoi(argv[i + 1]);
|
||||
if (args->timeout_sec <= 0) {
|
||||
fprintf(stderr, "Error: timeout_sec must be a positive integer\n");
|
||||
return -1;
|
||||
}
|
||||
i++; // Skip the next argument
|
||||
@@ -204,13 +237,44 @@ static char* read_stdin_json(void) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Mining thread function
|
||||
// Callback implementations
|
||||
static void solution_found_callback(cJSON* solution, void* user_data) {
|
||||
main_context_t* main_ctx = (main_context_t*)user_data;
|
||||
|
||||
pthread_mutex_lock(&main_ctx->result_mutex);
|
||||
if (!main_ctx->solution_found) {
|
||||
main_ctx->solution_found = 1;
|
||||
main_ctx->result_event = cJSON_Duplicate(solution, 1);
|
||||
}
|
||||
pthread_mutex_unlock(&main_ctx->result_mutex);
|
||||
}
|
||||
|
||||
static void progress_report_callback(int thread_id, uint64_t attempts, void* user_data) {
|
||||
// Progress callback - placeholder for future implementation
|
||||
// For now, do nothing as requested
|
||||
(void)thread_id; // Suppress unused parameter warning
|
||||
(void)attempts; // Suppress unused parameter warning
|
||||
(void)user_data; // Suppress unused parameter warning
|
||||
}
|
||||
|
||||
static void error_report_callback(int thread_id, int error_code, void* user_data) {
|
||||
// Error callback for debugging - placeholder for future implementation
|
||||
// For now, do nothing but could be used for debugging thread issues
|
||||
(void)thread_id; // Suppress unused parameter warning
|
||||
(void)error_code; // Suppress unused parameter warning
|
||||
(void)user_data; // Suppress unused parameter warning
|
||||
}
|
||||
|
||||
// Mining thread function - New callback-based approach
|
||||
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) {
|
||||
if (ctx->error_callback) {
|
||||
ctx->error_callback(ctx->thread_id, -1, ctx->user_data);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -218,106 +282,149 @@ static void* miner_thread(void* arg) {
|
||||
free(event_str);
|
||||
|
||||
if (!local_event) {
|
||||
if (ctx->error_callback) {
|
||||
ctx->error_callback(ctx->thread_id, -2, ctx->user_data);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t attempts = 0;
|
||||
|
||||
// Mine until solution found or signaled to stop by main thread
|
||||
while (!ctx->should_stop) {
|
||||
// Attempt mining
|
||||
int result = nostr_add_proof_of_work(local_event, ctx->private_key,
|
||||
ctx->target_difficulty, NULL, NULL);
|
||||
|
||||
attempts++;
|
||||
|
||||
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);
|
||||
// Found solution - report to main thread via callback
|
||||
if (ctx->solution_callback) {
|
||||
ctx->solution_callback(local_event, ctx->user_data);
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->mutex);
|
||||
break;
|
||||
break; // Exit after reporting solution
|
||||
}
|
||||
|
||||
// Small delay to prevent CPU overuse
|
||||
usleep(1000); // 1ms
|
||||
// Progress reporting (currently disabled but ready for future use)
|
||||
if (ctx->progress_callback && attempts % 10000 == 0) {
|
||||
ctx->progress_callback(ctx->thread_id, attempts, ctx->user_data);
|
||||
}
|
||||
|
||||
// Small delay to prevent CPU overuse and allow responsive stopping
|
||||
usleep(100); // 0.1ms - more responsive to should_stop signal
|
||||
}
|
||||
|
||||
cJSON_Delete(local_event);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Main mining function
|
||||
// Main mining function - New hub-and-spoke model
|
||||
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");
|
||||
// Set up main context for centralized control
|
||||
main_context_t main_ctx;
|
||||
memset(&main_ctx, 0, sizeof(main_context_t));
|
||||
|
||||
// Initialize result mutex
|
||||
if (pthread_mutex_init(&main_ctx.result_mutex, NULL) != 0) {
|
||||
fprintf(stderr, "Error: Failed to initialize result 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);
|
||||
// Set up callback system
|
||||
ctx->solution_callback = solution_found_callback;
|
||||
ctx->progress_callback = progress_report_callback;
|
||||
ctx->error_callback = error_report_callback;
|
||||
ctx->user_data = &main_ctx;
|
||||
ctx->should_stop = 0;
|
||||
|
||||
// Create individual worker contexts (each gets its own thread_id)
|
||||
mining_context_t* worker_contexts = malloc(ctx->thread_count * sizeof(mining_context_t));
|
||||
if (!worker_contexts) {
|
||||
fprintf(stderr, "Error: Memory allocation failed for worker contexts\n");
|
||||
pthread_mutex_destroy(&main_ctx.result_mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Copy base context to each worker and set thread_id
|
||||
for (int i = 0; i < ctx->thread_count; i++) {
|
||||
memcpy(&worker_contexts[i], ctx, sizeof(mining_context_t));
|
||||
worker_contexts[i].thread_id = i;
|
||||
}
|
||||
|
||||
// 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);
|
||||
fprintf(stderr, "Error: Memory allocation failed for threads\n");
|
||||
free(worker_contexts);
|
||||
pthread_mutex_destroy(&main_ctx.result_mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->start_time = time(NULL);
|
||||
time_t 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) {
|
||||
if (pthread_create(&threads[i], NULL, miner_thread, &worker_contexts[i]) != 0) {
|
||||
fprintf(stderr, "Error: Failed to create thread %d\n", i);
|
||||
// Clean up already created threads
|
||||
ctx->found = -2; // Error flag
|
||||
// Stop already running threads
|
||||
for (int j = 0; j < ctx->thread_count; j++) {
|
||||
worker_contexts[j].should_stop = 1;
|
||||
}
|
||||
// Wait for threads that were created
|
||||
for (int j = 0; j < i; j++) {
|
||||
pthread_join(threads[j], NULL);
|
||||
}
|
||||
free(threads);
|
||||
pthread_mutex_destroy(&ctx->mutex);
|
||||
pthread_cond_destroy(&ctx->cond);
|
||||
free(worker_contexts);
|
||||
pthread_mutex_destroy(&main_ctx.result_mutex);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for solution or timeout
|
||||
pthread_mutex_lock(&ctx->mutex);
|
||||
while (ctx->found == 0) {
|
||||
pthread_cond_wait(&ctx->cond, &ctx->mutex);
|
||||
// Main thread control loop - centralized monitoring
|
||||
int result = 0;
|
||||
while (!main_ctx.solution_found && !main_ctx.timeout_reached) {
|
||||
// Check for timeout
|
||||
if (ctx->timeout_seconds > 0) {
|
||||
time_t current_time = time(NULL);
|
||||
if (current_time - start_time >= ctx->timeout_seconds) {
|
||||
main_ctx.timeout_reached = 1;
|
||||
result = -1; // Timeout
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Small sleep to avoid busy waiting
|
||||
usleep(10000); // 10ms
|
||||
}
|
||||
|
||||
// Signal all workers to stop
|
||||
for (int i = 0; i < ctx->thread_count; i++) {
|
||||
worker_contexts[i].should_stop = 1;
|
||||
}
|
||||
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);
|
||||
// Handle results
|
||||
if (main_ctx.solution_found && main_ctx.result_event) {
|
||||
ctx->result_event = main_ctx.result_event; // Transfer ownership
|
||||
result = 1; // Success
|
||||
} else if (main_ctx.timeout_reached) {
|
||||
result = -1; // Timeout
|
||||
} else {
|
||||
result = -2; // Error
|
||||
}
|
||||
|
||||
return ctx->found;
|
||||
// Cleanup
|
||||
free(threads);
|
||||
free(worker_contexts);
|
||||
pthread_mutex_destroy(&main_ctx.result_mutex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Cleanup context
|
||||
@@ -393,7 +500,7 @@ int main(int argc, char* argv[]) {
|
||||
// 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;
|
||||
ctx.timeout_seconds = args.timeout_sec > 0 ? args.timeout_sec : 0;
|
||||
|
||||
// Start mining
|
||||
int mining_result = mine_event(&ctx);
|
||||
@@ -409,7 +516,7 @@ int main(int argc, char* argv[]) {
|
||||
exit_code = 1;
|
||||
}
|
||||
} else if (mining_result == -1) {
|
||||
fprintf(stderr, "Error: Mining timeout reached\n");
|
||||
printf("timeout\n");
|
||||
exit_code = 1;
|
||||
} else {
|
||||
fprintf(stderr, "Error: Mining failed\n");
|
||||
|
||||
Reference in New Issue
Block a user