feat(c-miner): port miner pipeline to modular C implementation
This commit is contained in:
323
mining_loop.c
Normal file
323
mining_loop.c
Normal file
@@ -0,0 +1,323 @@
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user