#include "mining_loop.h" #include #include #include #include #include #include #include #include #include #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; }