#include "miner.h" #include #include #include #include #include #include "sha256/sha256_backend.h" #include "utils.h" static const double RATE_INTERVAL_SEC = 2.0; static inline uint32_t load_u32_be(const uint8_t *p) { return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3]; } static void target_words_from_be(const uint8_t target_be[32], uint32_t out[8]) { int i; for (i = 0; i < 8; i++) { out[i] = load_u32_be(target_be + i * 4); } } static inline void write_u32_le(uint8_t *dst, uint32_t v) { dst[0] = (uint8_t)(v & 0xFFU); dst[1] = (uint8_t)((v >> 8) & 0xFFU); dst[2] = (uint8_t)((v >> 16) & 0xFFU); dst[3] = (uint8_t)((v >> 24) & 0xFFU); } static int compute_hash_batch( const uint8_t header_76[76], uint32_t start_nonce, uint32_t batch_size, const uint32_t target_words[8], uint32_t *found_nonce, uint8_t found_digest[32] ) { sha256d80_midstate_t mid; uint32_t i = 0; sha256d80_midstate_init(&mid, header_76); /* Backend is now initialized; use direct scan to skip per-call pthread_once */ sha256_backend_ensure_init(); while ((batch_size - i) >= 4U) { uint32_t n0 = start_nonce + i; sha256_state_t st1[4]; uint32_t mask = sha256d80_scan_4way_direct(&mid, n0, target_words, st1); if (mask & 1U) { *found_nonce = n0; sha256_state_to_digest(&st1[0], found_digest); return 1; } if (mask & 2U) { *found_nonce = n0 + 1U; sha256_state_to_digest(&st1[1], found_digest); return 1; } if (mask & 4U) { *found_nonce = n0 + 2U; sha256_state_to_digest(&st1[2], found_digest); return 1; } if (mask & 8U) { *found_nonce = n0 + 3U; sha256_state_to_digest(&st1[3], found_digest); return 1; } i += 4U; } if (i < batch_size) { uint32_t n0 = start_nonce + i; uint32_t remain = batch_size - i; uint32_t valid_mask = (1U << remain) - 1U; sha256_state_t st1[4]; uint32_t mask = sha256d80_scan_4way(&mid, n0, target_words, st1) & valid_mask; if (mask & 1U) { *found_nonce = n0; sha256_state_to_digest(&st1[0], found_digest); return 1; } if (mask & 2U) { *found_nonce = n0 + 1U; sha256_state_to_digest(&st1[1], found_digest); return 1; } if (mask & 4U) { *found_nonce = n0 + 2U; sha256_state_to_digest(&st1[2], found_digest); return 1; } if (mask & 8U) { *found_nonce = n0 + 3U; sha256_state_to_digest(&st1[3], found_digest); return 1; } } return 0; } int mine_block( uint8_t header_76[76], const char *target_hex, const char *nonce_mode, uint32_t batch_size, int timestamp_update_interval, atomic_int *stop_flag, mine_status_cb status_cb, void *status_ctx, MineResult *out ) { uint8_t target_be[32]; uint32_t target_words[8]; uint32_t nonce; long long attempts = 0; struct timespec ts_start, ts_now; double last_rate_t; long long last_rate_n = 0; double last_tsu; uint32_t batch_count = 0; /* Check timestamp update every 32 batches, rate every 32 batches (~1s) */ #define TSU_CHECK_MASK 31U #define RATE_CHECK_MASK 31U memset(out, 0, sizeof(*out)); if (hex_to_fixed_bytes(target_hex, target_be, 32) == 0) { fprintf(stderr, "[miner] target hex non valido\n"); return 0; } target_words_from_be(target_be, target_words); if (strcmp(nonce_mode, "incremental") == 0) { nonce = 0; } else { nonce = (uint32_t)rand(); } clock_gettime(CLOCK_MONOTONIC_COARSE, &ts_start); last_rate_t = (double)ts_start.tv_sec + (double)ts_start.tv_nsec * 1e-9; last_tsu = last_rate_t; while (1) { uint32_t found_nonce = 0; uint8_t found_digest[32]; if (atomic_load(stop_flag) != 0) { return 0; } batch_count++; /* Timestamp update: check every 64 batches to reduce clock calls */ if (timestamp_update_interval > 0 && (batch_count & TSU_CHECK_MASK) == 0) { clock_gettime(CLOCK_MONOTONIC_COARSE, &ts_now); double now = (double)ts_now.tv_sec + (double)ts_now.tv_nsec * 1e-9; if (now - last_tsu >= (double)timestamp_update_interval) { write_u32_le(header_76 + 68, (uint32_t)ts_now.tv_sec); last_tsu = now; } } if (compute_hash_batch(header_76, nonce, batch_size, target_words, &found_nonce, found_digest)) { clock_gettime(CLOCK_MONOTONIC_COARSE, &ts_now); double total = ((double)ts_now.tv_sec + (double)ts_now.tv_nsec * 1e-9) - ((double)ts_start.tv_sec + (double)ts_start.tv_nsec * 1e-9); if (total <= 0.0) { total = 1e-6; } out->found = 1; out->nonce = found_nonce; out->attempts = attempts + batch_size; out->hashrate_hz = (double)out->attempts / total; memcpy(out->header, header_76, 76); write_u32_le(out->header + 76, found_nonce); memcpy(out->digest, found_digest, 32); return 1; } attempts += batch_size; nonce += batch_size; /* Rate reporting: check every 128 batches */ if ((batch_count & RATE_CHECK_MASK) == 0) { clock_gettime(CLOCK_MONOTONIC_COARSE, &ts_now); double now = (double)ts_now.tv_sec + (double)ts_now.tv_nsec * 1e-9; if (now - last_rate_t >= RATE_INTERVAL_SEC) { double dt = now - last_rate_t; if (dt <= 0.0) { dt = 1e-6; } if (status_cb != NULL) { double hr = (double)(attempts - last_rate_n) / dt; status_cb(attempts, hr, status_ctx); } last_rate_t = now; last_rate_n = attempts; } } } #undef TSU_CHECK_MASK #undef RATE_CHECK_MASK }