feat(c-miner): port miner pipeline to modular C implementation
This commit is contained in:
422
launcher.c
Normal file
422
launcher.c
Normal file
@@ -0,0 +1,422 @@
|
||||
#include <poll.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "mining_loop.h"
|
||||
#include "rpc.h"
|
||||
#include "utils.h"
|
||||
|
||||
typedef struct {
|
||||
pid_t pid;
|
||||
int fd;
|
||||
char buf[4096];
|
||||
size_t len;
|
||||
} WorkerProc;
|
||||
|
||||
typedef struct {
|
||||
double *rates;
|
||||
long long *attempts;
|
||||
char block_hash[130];
|
||||
int winner_idx;
|
||||
double winner_rate;
|
||||
int has_winner_rate;
|
||||
int lines_printed;
|
||||
double start_t;
|
||||
double last_print;
|
||||
} DashboardState;
|
||||
|
||||
static double now_seconds(void) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
|
||||
}
|
||||
|
||||
static void clear_lines(int n) {
|
||||
int i;
|
||||
for (i = 0; i < n; i++) {
|
||||
fputs("\033[F\033[K", stdout);
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static void dashboard_print(DashboardState *st, int n) {
|
||||
int i;
|
||||
double total_rate = 0.0;
|
||||
long long total_attempts = 0;
|
||||
time_t now_t = time(NULL);
|
||||
char ts[64];
|
||||
|
||||
if (st->lines_printed > 0) {
|
||||
clear_lines(st->lines_printed);
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
total_rate += st->rates[i];
|
||||
total_attempts += st->attempts[i];
|
||||
}
|
||||
|
||||
strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S", localtime(&now_t));
|
||||
|
||||
printf("%s | MINING STATUS\n", ts);
|
||||
printf("========================================\n");
|
||||
printf("Total: %.2f kH/s | Attempts: %lld\n", total_rate, total_attempts);
|
||||
printf("----------------------------------------\n");
|
||||
for (i = 0; i < n; i++) {
|
||||
printf("Worker %-2d: %.2f kH/s | Attempts: %lld\n", i, st->rates[i], st->attempts[i]);
|
||||
}
|
||||
|
||||
st->lines_printed = 4 + n;
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static int parse_event_line(const char *line, DashboardState *st, int n) {
|
||||
int idx;
|
||||
double rate;
|
||||
long long attempts;
|
||||
char hash[129];
|
||||
|
||||
if (sscanf(line, "status %d %lf %lld", &idx, &rate, &attempts) == 3) {
|
||||
if (idx >= 0 && idx < n) {
|
||||
st->rates[idx] = rate;
|
||||
st->attempts[idx] = attempts;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sscanf(line, "found %d %lf", &idx, &rate) == 2) {
|
||||
st->winner_idx = idx;
|
||||
st->winner_rate = rate;
|
||||
st->has_winner_rate = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sscanf(line, "hash %d %128s", &idx, hash) == 2) {
|
||||
snprintf(st->block_hash, sizeof(st->block_hash), "%s", hash);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sscanf(line, "submit %d", &idx) == 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_worker_bytes(WorkerProc *w, DashboardState *st, int n) {
|
||||
char tmp[512];
|
||||
ssize_t r;
|
||||
|
||||
r = read(w->fd, tmp, sizeof(tmp));
|
||||
if (r < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (r == 0) {
|
||||
close(w->fd);
|
||||
w->fd = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (w->len + (size_t)r >= sizeof(w->buf)) {
|
||||
w->len = 0;
|
||||
}
|
||||
|
||||
memcpy(w->buf + w->len, tmp, (size_t)r);
|
||||
w->len += (size_t)r;
|
||||
|
||||
while (1) {
|
||||
char *nl = memchr(w->buf, '\n', w->len);
|
||||
size_t line_len;
|
||||
char line[512];
|
||||
int rc;
|
||||
|
||||
if (nl == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
line_len = (size_t)(nl - w->buf);
|
||||
if (line_len >= sizeof(line)) {
|
||||
line_len = sizeof(line) - 1;
|
||||
}
|
||||
|
||||
memcpy(line, w->buf, line_len);
|
||||
line[line_len] = '\0';
|
||||
|
||||
memmove(w->buf, nl + 1, w->len - (line_len + 1));
|
||||
w->len -= (line_len + 1);
|
||||
|
||||
rc = parse_event_line(line, st, n);
|
||||
if (rc == 1) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aggregate_loop(WorkerProc *workers, int n) {
|
||||
struct pollfd *pfds;
|
||||
DashboardState st;
|
||||
int i;
|
||||
|
||||
memset(&st, 0, sizeof(st));
|
||||
st.rates = (double *)calloc((size_t)n, sizeof(double));
|
||||
st.attempts = (long long *)calloc((size_t)n, sizeof(long long));
|
||||
st.winner_idx = -1;
|
||||
st.start_t = now_seconds();
|
||||
st.last_print = 0.0;
|
||||
|
||||
if (st.rates == NULL || st.attempts == NULL) {
|
||||
free(st.rates);
|
||||
free(st.attempts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pfds = (struct pollfd *)calloc((size_t)n, sizeof(struct pollfd));
|
||||
if (pfds == NULL) {
|
||||
free(st.rates);
|
||||
free(st.attempts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int active = 0;
|
||||
double now = now_seconds();
|
||||
int submit_seen = 0;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
pfds[i].fd = workers[i].fd;
|
||||
pfds[i].events = (workers[i].fd >= 0) ? POLLIN : 0;
|
||||
pfds[i].revents = 0;
|
||||
if (workers[i].fd >= 0) {
|
||||
active++;
|
||||
}
|
||||
}
|
||||
|
||||
if (active == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
poll(pfds, (nfds_t)n, 200);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (workers[i].fd >= 0 && (pfds[i].revents & POLLIN)) {
|
||||
if (process_worker_bytes(&workers[i], &st, n)) {
|
||||
submit_seen = 1;
|
||||
}
|
||||
}
|
||||
if (workers[i].fd >= 0 && (pfds[i].revents & (POLLHUP | POLLERR | POLLNVAL))) {
|
||||
close(workers[i].fd);
|
||||
workers[i].fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (now - st.last_print >= 1.0) {
|
||||
dashboard_print(&st, n);
|
||||
st.last_print = now;
|
||||
}
|
||||
|
||||
if (submit_seen) {
|
||||
double elapsed = now_seconds() - st.start_t;
|
||||
long long total_attempts = 0;
|
||||
double avg_rate;
|
||||
|
||||
if (st.lines_printed > 0) {
|
||||
clear_lines(st.lines_printed);
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
total_attempts += st.attempts[i];
|
||||
}
|
||||
|
||||
avg_rate = (elapsed > 0.0) ? (double)total_attempts / elapsed / 1000.0 : 0.0;
|
||||
|
||||
printf("==============================================================================\n");
|
||||
printf("[OK] BLOCK FOUND AND SUBMITTED\n");
|
||||
printf(" Hash: %s\n", st.block_hash[0] ? st.block_hash : "N/A");
|
||||
if (st.winner_idx >= 0) {
|
||||
printf(" Worker: %d\n", st.winner_idx);
|
||||
}
|
||||
if (st.has_winner_rate) {
|
||||
printf(" Worker hashrate: %.2f kH/s\n", st.winner_rate);
|
||||
}
|
||||
printf(" Average total hashrate: %.2f kH/s\n", avg_rate);
|
||||
printf(" Total attempts: %lld\n", total_attempts);
|
||||
printf("==============================================================================\n");
|
||||
|
||||
free(pfds);
|
||||
free(st.rates);
|
||||
free(st.attempts);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
free(pfds);
|
||||
free(st.rates);
|
||||
free(st.attempts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void terminate_workers(WorkerProc *workers, int n) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (workers[i].pid > 0) {
|
||||
kill(workers[i].pid, SIGTERM);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (workers[i].pid > 0) {
|
||||
waitpid(workers[i].pid, NULL, 0);
|
||||
}
|
||||
if (workers[i].fd >= 0) {
|
||||
close(workers[i].fd);
|
||||
workers[i].fd = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int spawn_worker(WorkerProc *w, int idx, const MinerConfig *cfg, const char *base_ex2) {
|
||||
int p[2];
|
||||
pid_t pid;
|
||||
|
||||
if (pipe(p) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
close(p[0]);
|
||||
close(p[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
char *ex2;
|
||||
close(p[0]);
|
||||
|
||||
#ifdef __linux__
|
||||
{
|
||||
cpu_set_t set;
|
||||
CPU_ZERO(&set);
|
||||
CPU_SET(idx % (int)sysconf(_SC_NPROCESSORS_ONLN), &set);
|
||||
sched_setaffinity(0, sizeof(set), &set);
|
||||
}
|
||||
#endif
|
||||
|
||||
ex2 = hex_add_width(base_ex2, idx);
|
||||
if (ex2 == NULL) {
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
srand((unsigned int)(time(NULL) ^ getpid()));
|
||||
run_mining_loop(cfg, idx, ex2, p[1]);
|
||||
free(ex2);
|
||||
close(p[1]);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
close(p[1]);
|
||||
|
||||
w->pid = pid;
|
||||
w->fd = p[0];
|
||||
w->len = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void usage(const char *prog) {
|
||||
fprintf(stderr, "Uso: %s [--config miner.conf] [-n NUM_PROCS] [--base-extranonce2 HEX]\n", prog);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
const char *config_path = "miner.conf";
|
||||
int num_procs = -1;
|
||||
const char *base_ex2_cli = NULL;
|
||||
MinerConfig cfg;
|
||||
RpcClient rpc;
|
||||
char *chain = NULL;
|
||||
char *base_ex2;
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if ((strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--num-procs") == 0) && i + 1 < argc) {
|
||||
num_procs = atoi(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(argv[i], "--base-extranonce2") == 0 && i + 1 < argc) {
|
||||
base_ex2_cli = argv[++i];
|
||||
continue;
|
||||
}
|
||||
if (strcmp(argv[i], "--config") == 0 && i + 1 < argc) {
|
||||
config_path = argv[++i];
|
||||
continue;
|
||||
}
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (config_load(config_path, &cfg) == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (num_procs <= 0) {
|
||||
num_procs = cfg.num_processors;
|
||||
}
|
||||
|
||||
base_ex2 = (char *)(base_ex2_cli ? base_ex2_cli : cfg.extranonce2);
|
||||
|
||||
rpc_init(&rpc, &cfg);
|
||||
if (rpc_test_connection(&rpc, &chain) == 0) {
|
||||
fprintf(stderr, "[launcher] RPC test fallito\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("\nStarting mining with %d processes (chain=%s, extranonce2 base=%s)\n\n", num_procs, chain, base_ex2);
|
||||
free(chain);
|
||||
|
||||
while (1) {
|
||||
WorkerProc *workers = (WorkerProc *)calloc((size_t)num_procs, sizeof(WorkerProc));
|
||||
int ok = 1;
|
||||
int restart;
|
||||
|
||||
if (workers == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_procs; i++) {
|
||||
if (spawn_worker(&workers[i], i, &cfg, base_ex2) == 0) {
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok == 0) {
|
||||
fprintf(stderr, "[launcher] errore nello spawn worker\n");
|
||||
terminate_workers(workers, num_procs);
|
||||
free(workers);
|
||||
return 1;
|
||||
}
|
||||
|
||||
restart = aggregate_loop(workers, num_procs);
|
||||
terminate_workers(workers, num_procs);
|
||||
free(workers);
|
||||
|
||||
if (restart == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
printf("\nRestarting workers...\n\n");
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user