330 lines
8.8 KiB
C
330 lines
8.8 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;
|
|
}
|
|
|
|
if (event_fd < 0) {
|
|
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++;
|
|
}
|
|
}
|
|
|
|
if (event_fd < 0) {
|
|
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 (event_fd < 0 && 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) {
|
|
if (event_fd < 0) {
|
|
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;
|
|
}
|