Files
cpu-miner/mining_loop.c

324 lines
8.7 KiB
C

#include "mining_loop.h"
#include <pthread.h>
#include <stdarg.h>
#include <stdatomic.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "block_builder.h"
#include "miner.h"
#include "rpc.h"
#include "types.h"
#include "utils.h"
typedef struct {
int event_fd;
int worker_idx;
int single_mode;
} StatusCtx;
typedef struct {
RpcClient rpc;
atomic_int *stop_flag;
atomic_int *new_block_flag;
int check_interval;
} WatchdogCtx;
static void emit_eventf(int fd, const char *fmt, ...) {
char line[512];
va_list ap;
int n;
if (fd < 0) {
return;
}
va_start(ap, fmt);
n = vsnprintf(line, sizeof(line), fmt, ap);
va_end(ap);
if (n > 0) {
size_t len = (size_t)n;
if (len > sizeof(line) - 1) {
len = sizeof(line) - 1;
}
write(fd, line, len);
}
}
static void status_callback(long long attempts, double hashrate_hz, void *ctx_ptr) {
StatusCtx *ctx = (StatusCtx *)ctx_ptr;
if (ctx->event_fd >= 0) {
emit_eventf(ctx->event_fd, "status %d %.6f %lld\n", ctx->worker_idx, hashrate_hz / 1000.0, attempts);
return;
}
if (ctx->single_mode) {
printf("\r[main] hashrate: %.2f kH/s | attempts: %lld", hashrate_hz / 1000.0, attempts);
fflush(stdout);
}
}
static void *watchdog_thread_main(void *arg) {
WatchdogCtx *ctx = (WatchdogCtx *)arg;
char *last_hash = NULL;
if (rpc_get_best_block_hash(&ctx->rpc, &last_hash) == 0) {
return NULL;
}
while (atomic_load(ctx->stop_flag) == 0) {
int i;
char *new_hash = NULL;
for (i = 0; i < ctx->check_interval; i++) {
if (atomic_load(ctx->stop_flag) != 0) {
break;
}
sleep(1);
}
if (atomic_load(ctx->stop_flag) != 0) {
break;
}
if (rpc_get_best_block_hash(&ctx->rpc, &new_hash)) {
if (new_hash != NULL && last_hash != NULL && strcmp(new_hash, last_hash) != 0) {
atomic_store(ctx->new_block_flag, 1);
atomic_store(ctx->stop_flag, 1);
free(last_hash);
last_hash = new_hash;
break;
}
free(new_hash);
}
}
free(last_hash);
return NULL;
}
int run_mining_loop(const MinerConfig *cfg, int worker_idx, const char *extranonce2, int event_fd) {
RpcClient rpc;
char *chain = NULL;
char *miner_script = NULL;
StatusCtx status_ctx;
rpc_init(&rpc, cfg);
if (rpc_test_connection(&rpc, &chain) == 0) {
fprintf(stderr, "[main] connessione RPC fallita\n");
return 1;
}
if (rpc_get_address_script_pubkey(&rpc, cfg->wallet_address, &miner_script) == 0) {
fprintf(stderr, "[main] impossibile leggere scriptPubKey per wallet\n");
free(chain);
return 1;
}
fprintf(stdout, "[main] chain=%s worker=%d extranonce2=%s\n", chain, worker_idx, extranonce2);
status_ctx.event_fd = event_fd;
status_ctx.worker_idx = worker_idx;
status_ctx.single_mode = (event_fd < 0) ? 1 : 0;
while (1) {
BlockTemplate tpl;
char *coinbase_hex = NULL;
char *coinbase_txid = NULL;
char *target_hex = NULL;
char *merkle_root = NULL;
uint8_t header80[80];
uint8_t header76[76];
atomic_int stop_flag;
atomic_int new_block_flag;
WatchdogCtx wctx;
pthread_t watchdog;
MineResult result;
char *header_hex = NULL;
char *block_hash = NULL;
char *serialized = NULL;
int witness_count = 0;
size_t i;
memset(&tpl, 0, sizeof(tpl));
if (rpc_get_block_template(&rpc, &tpl) == 0) {
fprintf(stderr, "[main] getblocktemplate fallita, retry in 5s\n");
sleep(5);
continue;
}
rpc_ensure_witness_data(&rpc, &tpl);
for (i = 0; i < tpl.tx_count; i++) {
if (is_segwit_tx(tpl.transactions[i].data)) {
witness_count++;
}
}
fprintf(stdout, "[main] template height=%d tx=%zu segwit=%d\n", tpl.height, tpl.tx_count, witness_count);
if (build_coinbase_transaction(
&tpl,
miner_script,
cfg->extranonce1,
extranonce2,
cfg->coinbase_message,
&coinbase_hex,
&coinbase_txid
) == 0) {
fprintf(stderr, "[main] build coinbase fallita\n");
block_template_free(&tpl);
sleep(1);
continue;
}
target_hex = calculate_target_hex(tpl.bits, cfg->difficulty_factor, chain);
merkle_root = calculate_merkle_root(coinbase_txid, tpl.transactions, tpl.tx_count);
if (target_hex == NULL || merkle_root == NULL) {
fprintf(stderr, "[main] target/merkle falliti\n");
free(coinbase_hex);
free(coinbase_txid);
free(target_hex);
free(merkle_root);
block_template_free(&tpl);
sleep(1);
continue;
}
if (build_block_header_bytes(
tpl.version,
tpl.previous_block_hash,
merkle_root,
(uint32_t)tpl.curtime,
tpl.bits,
0,
header80
) == 0) {
fprintf(stderr, "[main] build header fallito\n");
free(coinbase_hex);
free(coinbase_txid);
free(target_hex);
free(merkle_root);
block_template_free(&tpl);
sleep(1);
continue;
}
memcpy(header76, header80, 76);
atomic_init(&stop_flag, 0);
atomic_init(&new_block_flag, 0);
wctx.check_interval = cfg->check_interval;
wctx.stop_flag = &stop_flag;
wctx.new_block_flag = &new_block_flag;
rpc_init(&wctx.rpc, cfg);
pthread_create(&watchdog, NULL, watchdog_thread_main, &wctx);
if (mine_block(
header76,
target_hex,
cfg->nonce_mode,
cfg->batch,
cfg->timestamp_update_interval,
&stop_flag,
status_callback,
&status_ctx,
&result
) == 0) {
atomic_store(&stop_flag, 1);
pthread_join(watchdog, NULL);
if (status_ctx.single_mode) {
putchar('\n');
}
if (atomic_load(&new_block_flag) != 0) {
fprintf(stdout, "[main] nuovo best block: riavvio ciclo\n");
}
free(coinbase_hex);
free(coinbase_txid);
free(target_hex);
free(merkle_root);
block_template_free(&tpl);
continue;
}
atomic_store(&stop_flag, 1);
pthread_join(watchdog, NULL);
if (status_ctx.single_mode) {
putchar('\n');
}
header_hex = bytes_to_hex(result.header, 80);
if (header_hex == NULL) {
free(coinbase_hex);
free(coinbase_txid);
free(target_hex);
free(merkle_root);
block_template_free(&tpl);
sleep(1);
continue;
}
{
uint8_t dh[32];
uint8_t rev[32];
double_sha256(result.header, 80, dh);
memcpy(rev, dh, 32);
reverse_bytes(rev, 32);
block_hash = bytes_to_hex(rev, 32);
}
if (block_hash != NULL) {
fprintf(stdout, "[main] block trovato: %s\n", block_hash);
emit_eventf(event_fd, "found %d %.6f\n", worker_idx, result.hashrate_hz / 1000.0);
emit_eventf(event_fd, "hash %d %s\n", worker_idx, block_hash);
}
serialized = serialize_block(header_hex, coinbase_hex, tpl.transactions, tpl.tx_count);
if (serialized == NULL) {
fprintf(stderr, "[main] serializzazione blocco fallita\n");
free(block_hash);
free(header_hex);
free(coinbase_hex);
free(coinbase_txid);
free(target_hex);
free(merkle_root);
block_template_free(&tpl);
sleep(1);
continue;
}
rpc_submit_block(&rpc, serialized);
emit_eventf(event_fd, "submit %d\n", worker_idx);
free(serialized);
free(block_hash);
free(header_hex);
free(coinbase_hex);
free(coinbase_txid);
free(target_hex);
free(merkle_root);
block_template_free(&tpl);
sleep(1);
}
free(chain);
free(miner_script);
return 0;
}