431 lines
10 KiB
C
431 lines
10 KiB
C
#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];
|
|
char total_rate_buf[32];
|
|
|
|
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));
|
|
format_hashrate_khs(total_rate, total_rate_buf, sizeof(total_rate_buf));
|
|
|
|
printf("%s | MINING STATUS\n", ts);
|
|
printf("========================================\n");
|
|
printf("Total: %s | Attempts: %lld\n", total_rate_buf, total_attempts);
|
|
printf("----------------------------------------\n");
|
|
for (i = 0; i < n; i++) {
|
|
char worker_rate_buf[32];
|
|
format_hashrate_khs(st->rates[i], worker_rate_buf, sizeof(worker_rate_buf));
|
|
printf("Worker %-2d: %s | Attempts: %lld\n", i, worker_rate_buf, 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;
|
|
char winner_rate_buf[32];
|
|
char avg_rate_buf[32];
|
|
|
|
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;
|
|
format_hashrate_khs(avg_rate, avg_rate_buf, sizeof(avg_rate_buf));
|
|
|
|
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) {
|
|
format_hashrate_khs(st.winner_rate, winner_rate_buf, sizeof(winner_rate_buf));
|
|
printf(" Worker hashrate: %s\n", winner_rate_buf);
|
|
}
|
|
printf(" Average total hashrate: %s\n", avg_rate_buf);
|
|
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;
|
|
}
|