Compare commits
10 Commits
1501e6d65c
...
7f217df04f
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f217df04f | |||
| b6af554b0c | |||
| 8709072574 | |||
| 7d4096749a | |||
| b2f0090236 | |||
| b700b7b25d | |||
| 6be9e3cafd | |||
| 89dcee8951 | |||
| ef320a4397 | |||
| 5b4c11f6f0 |
25
.gitignore
vendored
25
.gitignore
vendored
@@ -1,8 +1,31 @@
|
|||||||
|
# Python/local environment (prototype legacy)
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
**/__pycache__/
|
||||||
venv/
|
venv/
|
||||||
.venv/
|
.venv/
|
||||||
|
.pytest_cache/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
|
||||||
|
# Local-only config
|
||||||
|
miner.conf
|
||||||
config.py
|
config.py
|
||||||
|
|
||||||
|
# C/C++ build artifacts
|
||||||
*.o
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Binaries produced by Makefile
|
||||||
miner
|
miner
|
||||||
launcher
|
launcher
|
||||||
miner.conf
|
bench_hash
|
||||||
|
test_sha256_backend
|
||||||
|
test_miner_regression
|
||||||
|
|
||||||
|
# Profiling / PGO artifacts
|
||||||
|
.pgo/
|
||||||
|
*.gcda
|
||||||
|
*.gcno
|
||||||
|
*.profraw
|
||||||
|
*.profdata
|
||||||
|
|||||||
53
Makefile
53
Makefile
@@ -1,10 +1,22 @@
|
|||||||
CC := gcc
|
CC := gcc
|
||||||
CFLAGS := -O3 -march=native -mtune=native -flto -fomit-frame-pointer -DNDEBUG -Wall -Wextra -std=c11 -D_GNU_SOURCE -D_POSIX_C_SOURCE=200809L
|
CFLAGS := -O3 -march=native -mtune=native -flto -fomit-frame-pointer -DNDEBUG -Wall -Wextra -std=c11 -D_GNU_SOURCE -D_POSIX_C_SOURCE=200809L
|
||||||
LDFLAGS := -pthread -lcrypto -lm -flto
|
LDFLAGS := -pthread -lcrypto -lm -flto
|
||||||
|
PGO_DIR := .pgo
|
||||||
|
|
||||||
COMMON_OBJS := config.o utils.o json.o rpc.o types.o block_builder.o miner.o mining_loop.o
|
SHA256_BACKEND_OBJS := sha256/sha256_backend.o
|
||||||
|
HAS_ARM_CRYPTO := $(shell echo | $(CC) $(CFLAGS) -dM -E - 2>/dev/null | grep -c __ARM_FEATURE_CRYPTO)
|
||||||
|
ifeq ($(shell uname -m),aarch64)
|
||||||
|
ifneq ($(HAS_ARM_CRYPTO),0)
|
||||||
|
SHA256_BACKEND_OBJS += sha256/sha256d80_4way_aarch64.o
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
.PHONY: all clean
|
COMMON_OBJS := config.o utils.o json.o rpc.o types.o block_builder.o miner.o mining_loop.o $(SHA256_BACKEND_OBJS)
|
||||||
|
BENCH_BIN := bench_hash
|
||||||
|
TEST_SHA_BIN := test_sha256_backend
|
||||||
|
TEST_MINER_BIN := test_miner_regression
|
||||||
|
|
||||||
|
.PHONY: all clean bench bench-5x test pgo-gen pgo-use pgo-clean
|
||||||
|
|
||||||
all: miner launcher
|
all: miner launcher
|
||||||
|
|
||||||
@@ -18,4 +30,39 @@ launcher: launcher.o $(COMMON_OBJS)
|
|||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o miner launcher
|
rm -f *.o sha256/*.o bench/*.o tests/*.o miner launcher $(BENCH_BIN) $(TEST_SHA_BIN) $(TEST_MINER_BIN)
|
||||||
|
|
||||||
|
bench: $(BENCH_BIN)
|
||||||
|
|
||||||
|
$(BENCH_BIN): bench/bench_hash.o $(SHA256_BACKEND_OBJS)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
|
bench-5x: $(BENCH_BIN)
|
||||||
|
@i=1; while [ $$i -le 5 ]; do \
|
||||||
|
echo "Run $$i/5"; \
|
||||||
|
./$(BENCH_BIN) 60; \
|
||||||
|
i=$$((i+1)); \
|
||||||
|
done
|
||||||
|
|
||||||
|
$(TEST_SHA_BIN): tests/test_sha256_backend.o $(SHA256_BACKEND_OBJS) utils.o
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
|
$(TEST_MINER_BIN): tests/test_miner_regression.o miner.o $(SHA256_BACKEND_OBJS) utils.o
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
|
test: $(TEST_SHA_BIN) $(TEST_MINER_BIN)
|
||||||
|
./$(TEST_SHA_BIN)
|
||||||
|
./$(TEST_MINER_BIN)
|
||||||
|
|
||||||
|
pgo-gen: CFLAGS += -fprofile-generate=$(PGO_DIR)
|
||||||
|
pgo-gen: LDFLAGS += -fprofile-generate=$(PGO_DIR)
|
||||||
|
pgo-gen: clean all
|
||||||
|
@echo "Profilazione build pronta."
|
||||||
|
@echo "Esegui il tuo workload manualmente, poi lancia: make pgo-use"
|
||||||
|
|
||||||
|
pgo-use: CFLAGS += -fprofile-use=$(PGO_DIR) -fprofile-correction
|
||||||
|
pgo-use: LDFLAGS += -fprofile-use=$(PGO_DIR)
|
||||||
|
pgo-use: clean all
|
||||||
|
|
||||||
|
pgo-clean:
|
||||||
|
rm -rf $(PGO_DIR)
|
||||||
|
|||||||
59
bench/bench_hash.c
Normal file
59
bench/bench_hash.c
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "../sha256/sha256_backend.h"
|
||||||
|
|
||||||
|
static double now_seconds(void) {
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
uint8_t header_76[76];
|
||||||
|
sha256d80_midstate_t mid;
|
||||||
|
sha256_state_t states[4];
|
||||||
|
uint32_t target_words[8];
|
||||||
|
uint32_t nonce = 0;
|
||||||
|
uint64_t hashes = 0;
|
||||||
|
volatile uint32_t sink = 0;
|
||||||
|
double seconds = 10.0;
|
||||||
|
double t0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
seconds = strtod(argv[1], NULL);
|
||||||
|
if (seconds <= 0.0) {
|
||||||
|
seconds = 10.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 76; i++) {
|
||||||
|
header_76[i] = (uint8_t)(i * 13 + 7);
|
||||||
|
}
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
target_words[i] = 0xFFFFFFFFU;
|
||||||
|
}
|
||||||
|
|
||||||
|
sha256d80_midstate_init(&mid, header_76);
|
||||||
|
|
||||||
|
t0 = now_seconds();
|
||||||
|
while ((now_seconds() - t0) < seconds) {
|
||||||
|
uint32_t mask = sha256d80_scan_4way(&mid, nonce, target_words, states);
|
||||||
|
|
||||||
|
sink ^= states[0].h[0] ^ states[1].h[0] ^ states[2].h[0] ^ states[3].h[0] ^ mask;
|
||||||
|
nonce += 4U;
|
||||||
|
hashes += 4U;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
double elapsed = now_seconds() - t0;
|
||||||
|
double khs = (double)hashes / elapsed / 1000.0;
|
||||||
|
printf("bench_hash: %.2f kH/s | hashes=%llu | sink=%08x\n", khs, (unsigned long long)hashes, sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
16
launcher.c
16
launcher.c
@@ -53,6 +53,7 @@ static void dashboard_print(DashboardState *st, int n) {
|
|||||||
long long total_attempts = 0;
|
long long total_attempts = 0;
|
||||||
time_t now_t = time(NULL);
|
time_t now_t = time(NULL);
|
||||||
char ts[64];
|
char ts[64];
|
||||||
|
char total_rate_buf[32];
|
||||||
|
|
||||||
if (st->lines_printed > 0) {
|
if (st->lines_printed > 0) {
|
||||||
clear_lines(st->lines_printed);
|
clear_lines(st->lines_printed);
|
||||||
@@ -64,13 +65,16 @@ static void dashboard_print(DashboardState *st, int n) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S", localtime(&now_t));
|
strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S", localtime(&now_t));
|
||||||
|
format_hashrate_khs(total_rate, total_rate_buf, sizeof(total_rate_buf));
|
||||||
|
|
||||||
printf("%s | MINING STATUS\n", ts);
|
printf("%s | MINING STATUS\n", ts);
|
||||||
printf("========================================\n");
|
printf("========================================\n");
|
||||||
printf("Total: %.2f kH/s | Attempts: %lld\n", total_rate, total_attempts);
|
printf("Total: %s | Attempts: %lld\n", total_rate_buf, total_attempts);
|
||||||
printf("----------------------------------------\n");
|
printf("----------------------------------------\n");
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
printf("Worker %-2d: %.2f kH/s | Attempts: %lld\n", i, st->rates[i], st->attempts[i]);
|
char worker_rate_buf[32];
|
||||||
|
format_hashrate_khs(st->rates[i], worker_rate_buf, sizeof(worker_rate_buf));
|
||||||
|
printf("Worker %-2d: %s | Attempts: %lld\n", i, worker_rate_buf, st->attempts[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
st->lines_printed = 4 + n;
|
st->lines_printed = 4 + n;
|
||||||
@@ -228,6 +232,8 @@ static int aggregate_loop(WorkerProc *workers, int n) {
|
|||||||
double elapsed = now_seconds() - st.start_t;
|
double elapsed = now_seconds() - st.start_t;
|
||||||
long long total_attempts = 0;
|
long long total_attempts = 0;
|
||||||
double avg_rate;
|
double avg_rate;
|
||||||
|
char winner_rate_buf[32];
|
||||||
|
char avg_rate_buf[32];
|
||||||
|
|
||||||
if (st.lines_printed > 0) {
|
if (st.lines_printed > 0) {
|
||||||
clear_lines(st.lines_printed);
|
clear_lines(st.lines_printed);
|
||||||
@@ -238,6 +244,7 @@ static int aggregate_loop(WorkerProc *workers, int n) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
avg_rate = (elapsed > 0.0) ? (double)total_attempts / elapsed / 1000.0 : 0.0;
|
avg_rate = (elapsed > 0.0) ? (double)total_attempts / elapsed / 1000.0 : 0.0;
|
||||||
|
format_hashrate_khs(avg_rate, avg_rate_buf, sizeof(avg_rate_buf));
|
||||||
|
|
||||||
printf("==============================================================================\n");
|
printf("==============================================================================\n");
|
||||||
printf("[OK] BLOCK FOUND AND SUBMITTED\n");
|
printf("[OK] BLOCK FOUND AND SUBMITTED\n");
|
||||||
@@ -246,9 +253,10 @@ static int aggregate_loop(WorkerProc *workers, int n) {
|
|||||||
printf(" Worker: %d\n", st.winner_idx);
|
printf(" Worker: %d\n", st.winner_idx);
|
||||||
}
|
}
|
||||||
if (st.has_winner_rate) {
|
if (st.has_winner_rate) {
|
||||||
printf(" Worker hashrate: %.2f kH/s\n", st.winner_rate);
|
format_hashrate_khs(st.winner_rate, winner_rate_buf, sizeof(winner_rate_buf));
|
||||||
|
printf(" Worker hashrate: %s\n", winner_rate_buf);
|
||||||
}
|
}
|
||||||
printf(" Average total hashrate: %.2f kH/s\n", avg_rate);
|
printf(" Average total hashrate: %s\n", avg_rate_buf);
|
||||||
printf(" Total attempts: %lld\n", total_attempts);
|
printf(" Total attempts: %lld\n", total_attempts);
|
||||||
printf("==============================================================================\n");
|
printf("==============================================================================\n");
|
||||||
|
|
||||||
|
|||||||
210
miner.c
210
miner.c
@@ -1,99 +1,106 @@
|
|||||||
#include "miner.h"
|
#include "miner.h"
|
||||||
|
|
||||||
#include <openssl/sha.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "sha256/sha256_backend.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
static const double RATE_INTERVAL_SEC = 5.0;
|
static const double RATE_INTERVAL_SEC = 2.0;
|
||||||
|
|
||||||
static int hash_meets_target(const uint8_t digest[32], const uint8_t target_be[32]) {
|
static inline uint32_t load_u32_be(const uint8_t *p) {
|
||||||
int i;
|
return ((uint32_t)p[0] << 24) |
|
||||||
|
((uint32_t)p[1] << 16) |
|
||||||
for (i = 0; i < 32; i++) {
|
((uint32_t)p[2] << 8) |
|
||||||
uint8_t hb = digest[31 - i];
|
(uint32_t)p[3];
|
||||||
if (hb < target_be[i]) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (hb > target_be[i]) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write_u32_be(uint8_t *dst, uint32_t v) {
|
static void target_words_from_be(const uint8_t target_be[32], uint32_t out[8]) {
|
||||||
dst[0] = (uint8_t)((v >> 24) & 0xFF);
|
|
||||||
dst[1] = (uint8_t)((v >> 16) & 0xFF);
|
|
||||||
dst[2] = (uint8_t)((v >> 8) & 0xFF);
|
|
||||||
dst[3] = (uint8_t)(v & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sha256_ctx_to_digest(const SHA256_CTX *ctx, uint8_t out[32]) {
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < 8; i++) {
|
for (i = 0; i < 8; i++) {
|
||||||
write_u32_be(out + i * 4, ctx->h[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(
|
static int compute_hash_batch(
|
||||||
const uint8_t header_76[76],
|
const uint8_t header_76[76],
|
||||||
uint32_t start_nonce,
|
uint32_t start_nonce,
|
||||||
uint32_t batch_size,
|
uint32_t batch_size,
|
||||||
const uint8_t target_be[32],
|
const uint32_t target_words[8],
|
||||||
uint32_t *found_nonce,
|
uint32_t *found_nonce,
|
||||||
uint8_t found_digest[32]
|
uint8_t found_digest[32]
|
||||||
) {
|
) {
|
||||||
SHA256_CTX init_ctx;
|
sha256d80_midstate_t mid;
|
||||||
SHA256_CTX first_chunk_ctx;
|
uint32_t i = 0;
|
||||||
uint8_t block1[64];
|
sha256d80_midstate_init(&mid, header_76);
|
||||||
uint8_t block2[64];
|
/* Backend is now initialized; use direct scan to skip per-call pthread_once */
|
||||||
uint8_t digest1[32];
|
sha256_backend_ensure_init();
|
||||||
uint8_t digest2[32];
|
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
SHA256_Init(&init_ctx);
|
while ((batch_size - i) >= 4U) {
|
||||||
first_chunk_ctx = init_ctx;
|
uint32_t n0 = start_nonce + i;
|
||||||
SHA256_Transform(&first_chunk_ctx, header_76);
|
sha256_state_t st1[4];
|
||||||
|
uint32_t mask = sha256d80_scan_4way_direct(&mid, n0, target_words, st1);
|
||||||
|
|
||||||
memset(block1, 0, sizeof(block1));
|
if (mask & 1U) {
|
||||||
memcpy(block1, header_76 + 64, 12);
|
*found_nonce = n0;
|
||||||
block1[16] = 0x80;
|
sha256_state_to_digest(&st1[0], found_digest);
|
||||||
block1[62] = 0x02;
|
return 1;
|
||||||
block1[63] = 0x80;
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
memset(block2, 0, sizeof(block2));
|
i += 4U;
|
||||||
block2[32] = 0x80;
|
}
|
||||||
block2[62] = 0x01;
|
|
||||||
block2[63] = 0x00;
|
|
||||||
|
|
||||||
for (i = 0; i < batch_size; i++) {
|
if (i < batch_size) {
|
||||||
SHA256_CTX ctx1;
|
uint32_t n0 = start_nonce + i;
|
||||||
SHA256_CTX ctx2;
|
uint32_t remain = batch_size - i;
|
||||||
uint32_t n = start_nonce + 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;
|
||||||
|
|
||||||
block1[12] = (uint8_t)(n & 0xFF);
|
if (mask & 1U) {
|
||||||
block1[13] = (uint8_t)((n >> 8) & 0xFF);
|
*found_nonce = n0;
|
||||||
block1[14] = (uint8_t)((n >> 16) & 0xFF);
|
sha256_state_to_digest(&st1[0], found_digest);
|
||||||
block1[15] = (uint8_t)((n >> 24) & 0xFF);
|
return 1;
|
||||||
|
}
|
||||||
ctx1 = first_chunk_ctx;
|
if (mask & 2U) {
|
||||||
SHA256_Transform(&ctx1, block1);
|
*found_nonce = n0 + 1U;
|
||||||
sha256_ctx_to_digest(&ctx1, digest1);
|
sha256_state_to_digest(&st1[1], found_digest);
|
||||||
|
return 1;
|
||||||
memcpy(block2, digest1, 32);
|
}
|
||||||
ctx2 = init_ctx;
|
if (mask & 4U) {
|
||||||
SHA256_Transform(&ctx2, block2);
|
*found_nonce = n0 + 2U;
|
||||||
sha256_ctx_to_digest(&ctx2, digest2);
|
sha256_state_to_digest(&st1[2], found_digest);
|
||||||
|
return 1;
|
||||||
if (hash_meets_target(digest2, target_be)) {
|
}
|
||||||
*found_nonce = n;
|
if (mask & 8U) {
|
||||||
memcpy(found_digest, digest2, 32);
|
*found_nonce = n0 + 3U;
|
||||||
|
sha256_state_to_digest(&st1[3], found_digest);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,13 +108,6 @@ static int compute_hash_batch(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write_u32_le(uint8_t *dst, uint32_t v) {
|
|
||||||
dst[0] = (uint8_t)(v & 0xFF);
|
|
||||||
dst[1] = (uint8_t)((v >> 8) & 0xFF);
|
|
||||||
dst[2] = (uint8_t)((v >> 16) & 0xFF);
|
|
||||||
dst[3] = (uint8_t)((v >> 24) & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mine_block(
|
int mine_block(
|
||||||
uint8_t header_76[76],
|
uint8_t header_76[76],
|
||||||
const char *target_hex,
|
const char *target_hex,
|
||||||
@@ -120,12 +120,17 @@ int mine_block(
|
|||||||
MineResult *out
|
MineResult *out
|
||||||
) {
|
) {
|
||||||
uint8_t target_be[32];
|
uint8_t target_be[32];
|
||||||
|
uint32_t target_words[8];
|
||||||
uint32_t nonce;
|
uint32_t nonce;
|
||||||
long long attempts = 0;
|
long long attempts = 0;
|
||||||
time_t t_start;
|
struct timespec ts_start, ts_now;
|
||||||
double last_rate_t;
|
double last_rate_t;
|
||||||
long long last_rate_n = 0;
|
long long last_rate_n = 0;
|
||||||
double last_tsu;
|
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));
|
memset(out, 0, sizeof(*out));
|
||||||
|
|
||||||
@@ -133,6 +138,7 @@ int mine_block(
|
|||||||
fprintf(stderr, "[miner] target hex non valido\n");
|
fprintf(stderr, "[miner] target hex non valido\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
target_words_from_be(target_be, target_words);
|
||||||
|
|
||||||
if (strcmp(nonce_mode, "incremental") == 0) {
|
if (strcmp(nonce_mode, "incremental") == 0) {
|
||||||
nonce = 0;
|
nonce = 0;
|
||||||
@@ -140,30 +146,34 @@ int mine_block(
|
|||||||
nonce = (uint32_t)rand();
|
nonce = (uint32_t)rand();
|
||||||
}
|
}
|
||||||
|
|
||||||
t_start = time(NULL);
|
clock_gettime(CLOCK_MONOTONIC_COARSE, &ts_start);
|
||||||
last_rate_t = (double)t_start;
|
last_rate_t = (double)ts_start.tv_sec + (double)ts_start.tv_nsec * 1e-9;
|
||||||
last_tsu = (double)t_start;
|
last_tsu = last_rate_t;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
uint32_t found_nonce = 0;
|
uint32_t found_nonce = 0;
|
||||||
uint8_t found_digest[32];
|
uint8_t found_digest[32];
|
||||||
time_t now_t;
|
|
||||||
double now;
|
|
||||||
|
|
||||||
if (atomic_load(stop_flag) != 0) {
|
if (atomic_load(stop_flag) != 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
now_t = time(NULL);
|
batch_count++;
|
||||||
now = (double)now_t;
|
|
||||||
|
|
||||||
if (timestamp_update_interval > 0 && (now - last_tsu) >= (double)timestamp_update_interval) {
|
/* Timestamp update: check every 64 batches to reduce clock calls */
|
||||||
write_u32_le(header_76 + 68, (uint32_t)now_t);
|
if (timestamp_update_interval > 0 && (batch_count & TSU_CHECK_MASK) == 0) {
|
||||||
last_tsu = now;
|
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_be, &found_nonce, found_digest)) {
|
if (compute_hash_batch(header_76, nonce, batch_size, target_words, &found_nonce, found_digest)) {
|
||||||
double total = now - (double)t_start;
|
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) {
|
if (total <= 0.0) {
|
||||||
total = 1e-6;
|
total = 1e-6;
|
||||||
}
|
}
|
||||||
@@ -182,18 +192,24 @@ int mine_block(
|
|||||||
attempts += batch_size;
|
attempts += batch_size;
|
||||||
nonce += batch_size;
|
nonce += batch_size;
|
||||||
|
|
||||||
now = (double)time(NULL);
|
/* Rate reporting: check every 128 batches */
|
||||||
if ((now - last_rate_t) >= RATE_INTERVAL_SEC) {
|
if ((batch_count & RATE_CHECK_MASK) == 0) {
|
||||||
double dt = now - last_rate_t;
|
clock_gettime(CLOCK_MONOTONIC_COARSE, &ts_now);
|
||||||
if (dt <= 0.0) {
|
double now = (double)ts_now.tv_sec + (double)ts_now.tv_nsec * 1e-9;
|
||||||
dt = 1e-6;
|
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;
|
||||||
}
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ static void emit_eventf(int fd, const char *fmt, ...) {
|
|||||||
|
|
||||||
static void status_callback(long long attempts, double hashrate_hz, void *ctx_ptr) {
|
static void status_callback(long long attempts, double hashrate_hz, void *ctx_ptr) {
|
||||||
StatusCtx *ctx = (StatusCtx *)ctx_ptr;
|
StatusCtx *ctx = (StatusCtx *)ctx_ptr;
|
||||||
|
char rate_buf[32];
|
||||||
|
|
||||||
if (ctx->event_fd >= 0) {
|
if (ctx->event_fd >= 0) {
|
||||||
emit_eventf(ctx->event_fd, "status %d %.6f %lld\n", ctx->worker_idx, hashrate_hz / 1000.0, attempts);
|
emit_eventf(ctx->event_fd, "status %d %.6f %lld\n", ctx->worker_idx, hashrate_hz / 1000.0, attempts);
|
||||||
@@ -60,7 +61,8 @@ static void status_callback(long long attempts, double hashrate_hz, void *ctx_pt
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->single_mode) {
|
if (ctx->single_mode) {
|
||||||
printf("\r[main] hashrate: %.2f kH/s | attempts: %lld", hashrate_hz / 1000.0, attempts);
|
format_hashrate_hz(hashrate_hz, rate_buf, sizeof(rate_buf));
|
||||||
|
printf("\r[main] hashrate: %s | attempts: %lld", rate_buf, attempts);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1046
sha256/sha256_backend.c
Normal file
1046
sha256/sha256_backend.c
Normal file
File diff suppressed because it is too large
Load Diff
83
sha256/sha256_backend.h
Normal file
83
sha256/sha256_backend.h
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#ifndef SHA256_BACKEND_H
|
||||||
|
#define SHA256_BACKEND_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compact SHA256 state: only the 8 chaining words (32 bytes).
|
||||||
|
* Avoids copying the bloated OpenSSL SHA256_CTX (~112 bytes) in the hot loop.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint32_t h[8];
|
||||||
|
} sha256_state_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sha256_state_t init_state;
|
||||||
|
sha256_state_t first_chunk_state;
|
||||||
|
uint8_t block1_template[64];
|
||||||
|
uint8_t block2_template[64];
|
||||||
|
} sha256d80_midstate_t;
|
||||||
|
|
||||||
|
/* Set state to SHA256 initial values (IV). */
|
||||||
|
void sha256_state_init(sha256_state_t *state);
|
||||||
|
|
||||||
|
/* Serialize the 8 state words into a 32-byte big-endian digest. */
|
||||||
|
void sha256_state_to_digest(const sha256_state_t *state, uint8_t out[32]);
|
||||||
|
|
||||||
|
/* Single SHA256 block compression (64-byte block). */
|
||||||
|
void sha256_transform_fast(sha256_state_t *state, const uint8_t block[64]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2-way interleaved SHA256 block compression.
|
||||||
|
* Processes two independent (state, block) pairs so the CPU can overlap both
|
||||||
|
* instruction chains. On non-ARM builds falls back to two sequential calls.
|
||||||
|
*/
|
||||||
|
void sha256_transform_fast_2way(
|
||||||
|
sha256_state_t *stA, const uint8_t blkA[64],
|
||||||
|
sha256_state_t *stB, const uint8_t blkB[64]
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Prepare SHA256d(80-byte header) midstate and constant blocks from header[0..75]. */
|
||||||
|
void sha256d80_midstate_init(sha256d80_midstate_t *mid, const uint8_t header_76[76]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hash 4 consecutive nonces with SHA256d(header80).
|
||||||
|
* start_nonce lane order: [n, n+1, n+2, n+3].
|
||||||
|
*/
|
||||||
|
void sha256d80_hash_4way(
|
||||||
|
const sha256d80_midstate_t *mid,
|
||||||
|
uint32_t start_nonce,
|
||||||
|
sha256_state_t out_states[4]
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hash 4 consecutive nonces and return hit mask against target words.
|
||||||
|
* target_words are big-endian words target[0..7].
|
||||||
|
* bit i set => lane i meets target.
|
||||||
|
*/
|
||||||
|
uint32_t sha256d80_scan_4way(
|
||||||
|
const sha256d80_midstate_t *mid,
|
||||||
|
uint32_t start_nonce,
|
||||||
|
const uint32_t target_words[8],
|
||||||
|
sha256_state_t out_states[4]
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure the SHA256 backend is initialized. Call once before using
|
||||||
|
* sha256d80_scan_4way_direct() to avoid per-call pthread_once overhead.
|
||||||
|
*/
|
||||||
|
void sha256_backend_ensure_init(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Like sha256d80_scan_4way() but skips the pthread_once check.
|
||||||
|
* Caller MUST have called sha256_backend_ensure_init() (or any other
|
||||||
|
* backend function) before calling this.
|
||||||
|
*/
|
||||||
|
uint32_t sha256d80_scan_4way_direct(
|
||||||
|
const sha256d80_midstate_t *mid,
|
||||||
|
uint32_t start_nonce,
|
||||||
|
const uint32_t target_words[8],
|
||||||
|
sha256_state_t out_states[4]
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif
|
||||||
9
sha256/sha256d80_4way_aarch64.S
Normal file
9
sha256/sha256d80_4way_aarch64.S
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
.text
|
||||||
|
.align 2
|
||||||
|
|
||||||
|
.global sha256d80_4way_aarch64_kernel
|
||||||
|
.type sha256d80_4way_aarch64_kernel, %function
|
||||||
|
sha256d80_4way_aarch64_kernel:
|
||||||
|
b sha256d80_4way_aarch64_impl
|
||||||
|
.size sha256d80_4way_aarch64_kernel, .-sha256d80_4way_aarch64_kernel
|
||||||
|
|
||||||
214
tests/test_miner_regression.c
Normal file
214
tests/test_miner_regression.c
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
#include <stdatomic.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../miner.h"
|
||||||
|
#include "../sha256/sha256_backend.h"
|
||||||
|
#include "../utils.h"
|
||||||
|
|
||||||
|
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 void sha256d_from_header76_nonce(const uint8_t header76[76], uint32_t nonce, uint8_t out[32]) {
|
||||||
|
uint8_t header80[80];
|
||||||
|
uint8_t block1[64];
|
||||||
|
uint8_t block2[64];
|
||||||
|
sha256_state_t init_state;
|
||||||
|
sha256_state_t st1;
|
||||||
|
sha256_state_t st2;
|
||||||
|
|
||||||
|
memcpy(header80, header76, 76);
|
||||||
|
write_u32_le(header80 + 76, nonce);
|
||||||
|
|
||||||
|
sha256_state_init(&init_state);
|
||||||
|
st1 = init_state;
|
||||||
|
sha256_transform_fast(&st1, header80);
|
||||||
|
|
||||||
|
memset(block1, 0, sizeof(block1));
|
||||||
|
memcpy(block1, header80 + 64, 16);
|
||||||
|
block1[16] = 0x80;
|
||||||
|
block1[62] = 0x02;
|
||||||
|
block1[63] = 0x80;
|
||||||
|
sha256_transform_fast(&st1, block1);
|
||||||
|
|
||||||
|
memset(block2, 0, sizeof(block2));
|
||||||
|
sha256_state_to_digest(&st1, block2);
|
||||||
|
block2[32] = 0x80;
|
||||||
|
block2[62] = 0x01;
|
||||||
|
block2[63] = 0x00;
|
||||||
|
|
||||||
|
st2 = init_state;
|
||||||
|
sha256_transform_fast(&st2, block2);
|
||||||
|
sha256_state_to_digest(&st2, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void digest_rev(const uint8_t digest[32], uint8_t rev[32]) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 32; i++) {
|
||||||
|
rev[i] = digest[31 - i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_record_nonce(
|
||||||
|
const uint8_t header76[76],
|
||||||
|
int parity,
|
||||||
|
uint32_t min_nonce,
|
||||||
|
uint32_t max_nonce,
|
||||||
|
uint32_t *out_nonce,
|
||||||
|
uint8_t out_target_be[32]
|
||||||
|
) {
|
||||||
|
uint8_t best[32];
|
||||||
|
uint32_t n;
|
||||||
|
|
||||||
|
memset(best, 0xFF, sizeof(best));
|
||||||
|
|
||||||
|
for (n = 0; n <= max_nonce; n++) {
|
||||||
|
uint8_t digest[32];
|
||||||
|
uint8_t rev[32];
|
||||||
|
|
||||||
|
sha256d_from_header76_nonce(header76, n, digest);
|
||||||
|
digest_rev(digest, rev);
|
||||||
|
|
||||||
|
if (memcmp(rev, best, 32) < 0) {
|
||||||
|
memcpy(best, rev, 32);
|
||||||
|
if (n >= min_nonce && ((int)(n & 1U) == parity)) {
|
||||||
|
*out_nonce = n;
|
||||||
|
memcpy(out_target_be, rev, 32);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int run_case(uint32_t batch, int parity) {
|
||||||
|
uint8_t header76[76];
|
||||||
|
uint8_t target_be[32];
|
||||||
|
uint8_t expected_digest[32];
|
||||||
|
MineResult out;
|
||||||
|
atomic_int stop_flag;
|
||||||
|
uint32_t expected_nonce;
|
||||||
|
char *target_hex;
|
||||||
|
int rc;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 76; i++) {
|
||||||
|
header76[i] = (uint8_t)((i * 29 + 11) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!find_record_nonce(header76, parity, 4U, 150000U, &expected_nonce, target_be)) {
|
||||||
|
fprintf(stderr, "[test_miner_regression] no record nonce found for parity=%d\n", parity);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
target_hex = bytes_to_hex(target_be, 32);
|
||||||
|
if (target_hex == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic_init(&stop_flag, 0);
|
||||||
|
rc = mine_block(
|
||||||
|
header76,
|
||||||
|
target_hex,
|
||||||
|
"incremental",
|
||||||
|
batch,
|
||||||
|
0,
|
||||||
|
&stop_flag,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&out
|
||||||
|
);
|
||||||
|
free(target_hex);
|
||||||
|
|
||||||
|
if (rc == 0 || out.found == 0) {
|
||||||
|
fprintf(stderr, "[test_miner_regression] mine_block failed for batch=%u\n", batch);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out.nonce != expected_nonce) {
|
||||||
|
fprintf(stderr, "[test_miner_regression] nonce mismatch batch=%u expected=%u got=%u\n",
|
||||||
|
batch, expected_nonce, out.nonce);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sha256d_from_header76_nonce(header76, out.nonce, expected_digest);
|
||||||
|
if (memcmp(expected_digest, out.digest, 32) != 0) {
|
||||||
|
fprintf(stderr, "[test_miner_regression] digest mismatch for nonce=%u\n", out.nonce);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_easy_target_finds_zero(void) {
|
||||||
|
uint8_t header76[76];
|
||||||
|
char *target_hex = NULL;
|
||||||
|
MineResult out;
|
||||||
|
atomic_int stop_flag;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 76; i++) {
|
||||||
|
header76[i] = (uint8_t)((i * 7 + 3) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
target_hex = bytes_to_hex((const uint8_t[32]){
|
||||||
|
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||||
|
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||||
|
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||||
|
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
|
||||||
|
}, 32);
|
||||||
|
if (target_hex == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic_init(&stop_flag, 0);
|
||||||
|
if (!mine_block(header76, target_hex, "incremental", 1, 0, &stop_flag, NULL, NULL, &out)) {
|
||||||
|
free(target_hex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
free(target_hex);
|
||||||
|
|
||||||
|
return out.found == 1 && out.nonce == 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_wrap_arithmetic(void) {
|
||||||
|
uint32_t start = 0xFFFFFFFEU;
|
||||||
|
uint32_t n0 = start + 0U;
|
||||||
|
uint32_t n1 = start + 1U;
|
||||||
|
uint32_t n2 = start + 2U;
|
||||||
|
uint32_t n3 = start + 3U;
|
||||||
|
|
||||||
|
return n0 == 0xFFFFFFFEU &&
|
||||||
|
n1 == 0xFFFFFFFFU &&
|
||||||
|
n2 == 0x00000000U &&
|
||||||
|
n3 == 0x00000001U;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
if (!test_easy_target_finds_zero()) {
|
||||||
|
fprintf(stderr, "test_easy_target_finds_zero: FAIL\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!run_case(5U, 1)) {
|
||||||
|
fprintf(stderr, "run_case(batch=5, parity=1): FAIL\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!run_case(6U, 0)) {
|
||||||
|
fprintf(stderr, "run_case(batch=6, parity=0): FAIL\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!test_wrap_arithmetic()) {
|
||||||
|
fprintf(stderr, "test_wrap_arithmetic: FAIL\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("test_miner_regression: OK\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
266
tests/test_sha256_backend.c
Normal file
266
tests/test_sha256_backend.c
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
#include <openssl/sha.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../sha256/sha256_backend.h"
|
||||||
|
#include "../utils.h"
|
||||||
|
|
||||||
|
static uint32_t xorshift32(uint32_t *s) {
|
||||||
|
uint32_t x = *s;
|
||||||
|
x ^= x << 13;
|
||||||
|
x ^= x >> 17;
|
||||||
|
x ^= x << 5;
|
||||||
|
*s = x;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 void fill_random(uint8_t *dst, size_t n, uint32_t *seed) {
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
dst[i] = (uint8_t)(xorshift32(seed) & 0xFFU);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void backend_double_sha256_80(const uint8_t header80[80], uint8_t out[32]) {
|
||||||
|
sha256_state_t init_state;
|
||||||
|
sha256_state_t st1;
|
||||||
|
sha256_state_t st2;
|
||||||
|
uint8_t block1[64];
|
||||||
|
uint8_t block2[64];
|
||||||
|
|
||||||
|
sha256_state_init(&init_state);
|
||||||
|
|
||||||
|
st1 = init_state;
|
||||||
|
sha256_transform_fast(&st1, header80);
|
||||||
|
|
||||||
|
memset(block1, 0, sizeof(block1));
|
||||||
|
memcpy(block1, header80 + 64, 16);
|
||||||
|
block1[16] = 0x80;
|
||||||
|
block1[62] = 0x02;
|
||||||
|
block1[63] = 0x80;
|
||||||
|
sha256_transform_fast(&st1, block1);
|
||||||
|
|
||||||
|
memset(block2, 0, sizeof(block2));
|
||||||
|
sha256_state_to_digest(&st1, block2);
|
||||||
|
block2[32] = 0x80;
|
||||||
|
block2[62] = 0x01;
|
||||||
|
block2[63] = 0x00;
|
||||||
|
|
||||||
|
st2 = init_state;
|
||||||
|
sha256_transform_fast(&st2, block2);
|
||||||
|
sha256_state_to_digest(&st2, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_transform_equivalence_random(void) {
|
||||||
|
uint32_t seed = 0x12345678U;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 10000; i++) {
|
||||||
|
uint8_t block[64];
|
||||||
|
SHA256_CTX ref;
|
||||||
|
sha256_state_t test;
|
||||||
|
|
||||||
|
fill_random(block, sizeof(block), &seed);
|
||||||
|
|
||||||
|
SHA256_Init(&ref);
|
||||||
|
sha256_state_init(&test);
|
||||||
|
SHA256_Transform(&ref, block);
|
||||||
|
sha256_transform_fast(&test, block);
|
||||||
|
|
||||||
|
if (memcmp(ref.h, test.h, sizeof(ref.h)) != 0) {
|
||||||
|
fprintf(stderr, "[test_sha256_backend] transform mismatch at iter=%d\n", i);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_transform_2way_equivalence(void) {
|
||||||
|
uint32_t seed = 0xCAFEBABEU;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 5000; i++) {
|
||||||
|
uint8_t block_a[64];
|
||||||
|
uint8_t block_b[64];
|
||||||
|
SHA256_CTX ref_a;
|
||||||
|
SHA256_CTX ref_b;
|
||||||
|
sha256_state_t test_a;
|
||||||
|
sha256_state_t test_b;
|
||||||
|
|
||||||
|
fill_random(block_a, sizeof(block_a), &seed);
|
||||||
|
fill_random(block_b, sizeof(block_b), &seed);
|
||||||
|
|
||||||
|
SHA256_Init(&ref_a);
|
||||||
|
SHA256_Init(&ref_b);
|
||||||
|
sha256_state_init(&test_a);
|
||||||
|
sha256_state_init(&test_b);
|
||||||
|
|
||||||
|
SHA256_Transform(&ref_a, block_a);
|
||||||
|
SHA256_Transform(&ref_b, block_b);
|
||||||
|
sha256_transform_fast_2way(&test_a, block_a, &test_b, block_b);
|
||||||
|
|
||||||
|
if (memcmp(ref_a.h, test_a.h, sizeof(ref_a.h)) != 0 ||
|
||||||
|
memcmp(ref_b.h, test_b.h, sizeof(ref_b.h)) != 0) {
|
||||||
|
fprintf(stderr, "[test_sha256_backend] 2way mismatch at iter=%d\n", i);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_double_sha256_80_equivalence(void) {
|
||||||
|
uint32_t seed = 0xA55AA55AU;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 2048; i++) {
|
||||||
|
uint8_t header80[80];
|
||||||
|
uint8_t ref[32];
|
||||||
|
uint8_t test[32];
|
||||||
|
|
||||||
|
fill_random(header80, sizeof(header80), &seed);
|
||||||
|
double_sha256(header80, sizeof(header80), ref);
|
||||||
|
backend_double_sha256_80(header80, test);
|
||||||
|
|
||||||
|
if (memcmp(ref, test, sizeof(ref)) != 0) {
|
||||||
|
fprintf(stderr, "[test_sha256_backend] sha256d80 mismatch at iter=%d\n", i);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_sha256d80_4way_100k_nonces(void) {
|
||||||
|
uint8_t header76[76];
|
||||||
|
sha256d80_midstate_t mid;
|
||||||
|
uint32_t base = 0x12340000U;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 76; i++) {
|
||||||
|
header76[i] = (uint8_t)((i * 19 + 5) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
sha256d80_midstate_init(&mid, header76);
|
||||||
|
|
||||||
|
for (i = 0; i < 25000; i++) {
|
||||||
|
uint32_t start_nonce = base + (uint32_t)(i * 4);
|
||||||
|
sha256_state_t states[4];
|
||||||
|
int lane;
|
||||||
|
|
||||||
|
sha256d80_hash_4way(&mid, start_nonce, states);
|
||||||
|
|
||||||
|
for (lane = 0; lane < 4; lane++) {
|
||||||
|
uint8_t header80[80];
|
||||||
|
uint8_t ref[32];
|
||||||
|
uint8_t got[32];
|
||||||
|
uint32_t nonce = start_nonce + (uint32_t)lane;
|
||||||
|
|
||||||
|
memcpy(header80, header76, 76);
|
||||||
|
write_u32_le(header80 + 76, nonce);
|
||||||
|
double_sha256(header80, sizeof(header80), ref);
|
||||||
|
sha256_state_to_digest(&states[lane], got);
|
||||||
|
|
||||||
|
if (memcmp(ref, got, 32) != 0) {
|
||||||
|
fprintf(stderr, "[test_sha256_backend] 4way nonce mismatch nonce=%u lane=%d\n", nonce, lane);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_sha256d80_scan_4way_direct(void) {
|
||||||
|
uint8_t header76[76];
|
||||||
|
sha256d80_midstate_t mid;
|
||||||
|
sha256_state_t st_ref[4];
|
||||||
|
sha256_state_t st_direct[4];
|
||||||
|
uint32_t all_max[8];
|
||||||
|
uint32_t mask_ref;
|
||||||
|
uint32_t mask_direct;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 76; i++) {
|
||||||
|
header76[i] = (uint8_t)((i * 13 + 7) & 0xFF);
|
||||||
|
}
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
all_max[i] = 0xFFFFFFFFU;
|
||||||
|
}
|
||||||
|
|
||||||
|
sha256d80_midstate_init(&mid, header76);
|
||||||
|
sha256_backend_ensure_init();
|
||||||
|
|
||||||
|
mask_ref = sha256d80_scan_4way(&mid, 0x11223344U, all_max, st_ref);
|
||||||
|
mask_direct = sha256d80_scan_4way_direct(&mid, 0x11223344U, all_max, st_direct);
|
||||||
|
|
||||||
|
if (mask_ref != mask_direct) {
|
||||||
|
fprintf(stderr, "[test_sha256_backend] scan_4way_direct mask mismatch ref=%u direct=%u\n",
|
||||||
|
mask_ref, mask_direct);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (memcmp(st_ref, st_direct, sizeof(st_ref)) != 0) {
|
||||||
|
fprintf(stderr, "[test_sha256_backend] scan_4way_direct state mismatch\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_sha256d80_scan_hitmask_basic(void) {
|
||||||
|
uint8_t header76[76];
|
||||||
|
sha256d80_midstate_t mid;
|
||||||
|
sha256_state_t states[4];
|
||||||
|
uint32_t all_max[8];
|
||||||
|
uint32_t all_zero[8];
|
||||||
|
uint32_t mask_max;
|
||||||
|
uint32_t mask_zero;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 76; i++) {
|
||||||
|
header76[i] = (uint8_t)((i * 11 + 1) & 0xFF);
|
||||||
|
}
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
all_max[i] = 0xFFFFFFFFU;
|
||||||
|
all_zero[i] = 0x00000000U;
|
||||||
|
}
|
||||||
|
|
||||||
|
sha256d80_midstate_init(&mid, header76);
|
||||||
|
mask_max = sha256d80_scan_4way(&mid, 0xABC00000U, all_max, states);
|
||||||
|
mask_zero = sha256d80_scan_4way(&mid, 0xABC00000U, all_zero, states);
|
||||||
|
|
||||||
|
return mask_max == 0xFU && mask_zero == 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
if (!test_transform_equivalence_random()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!test_transform_2way_equivalence()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!test_double_sha256_80_equivalence()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!test_sha256d80_4way_100k_nonces()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!test_sha256d80_scan_4way_direct()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!test_sha256d80_scan_hitmask_basic()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("test_sha256_backend: OK\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
42
utils.c
42
utils.c
@@ -15,6 +15,48 @@ static char to_hex_digit(unsigned int v) {
|
|||||||
return (char)('a' + (v - 10U));
|
return (char)('a' + (v - 10U));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void format_hashrate_core(double rate_hs, char *out, size_t out_len) {
|
||||||
|
static const char *units[] = {"H/s", "kH/s", "MH/s", "GH/s", "TH/s", "PH/s", "EH/s"};
|
||||||
|
size_t unit_idx = 0;
|
||||||
|
const size_t unit_count = sizeof(units) / sizeof(units[0]);
|
||||||
|
double value;
|
||||||
|
|
||||||
|
if (out == NULL || out_len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(rate_hs >= 0.0)) {
|
||||||
|
rate_hs = 0.0;
|
||||||
|
}
|
||||||
|
value = rate_hs;
|
||||||
|
|
||||||
|
while (value >= 999.95 && unit_idx + 1 < unit_count) {
|
||||||
|
value /= 1000.0;
|
||||||
|
unit_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (value > 0.0 && value < 1.0 && unit_idx > 0) {
|
||||||
|
value *= 1000.0;
|
||||||
|
unit_idx--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value >= 100.0) {
|
||||||
|
snprintf(out, out_len, "%.0f %s", value, units[unit_idx]);
|
||||||
|
} else if (value >= 10.0) {
|
||||||
|
snprintf(out, out_len, "%.1f %s", value, units[unit_idx]);
|
||||||
|
} else {
|
||||||
|
snprintf(out, out_len, "%.2f %s", value, units[unit_idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_hashrate_hz(double rate_hz, char *out, size_t out_len) {
|
||||||
|
format_hashrate_core(rate_hz, out, out_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_hashrate_khs(double rate_khs, char *out, size_t out_len) {
|
||||||
|
format_hashrate_core(rate_khs * 1000.0, out, out_len);
|
||||||
|
}
|
||||||
|
|
||||||
void sb_init(StrBuf *sb) {
|
void sb_init(StrBuf *sb) {
|
||||||
sb->data = NULL;
|
sb->data = NULL;
|
||||||
sb->len = 0;
|
sb->len = 0;
|
||||||
|
|||||||
2
utils.h
2
utils.h
@@ -33,5 +33,7 @@ char *decode_nbits_hex(uint32_t nbits);
|
|||||||
char *calculate_target_hex(const char *bits_hex, double difficulty_factor, const char *network);
|
char *calculate_target_hex(const char *bits_hex, double difficulty_factor, const char *network);
|
||||||
|
|
||||||
char *hex_add_width(const char *base_hex, int add_value);
|
char *hex_add_width(const char *base_hex, int add_value);
|
||||||
|
void format_hashrate_hz(double rate_hz, char *out, size_t out_len);
|
||||||
|
void format_hashrate_khs(double rate_khs, char *out, size_t out_len);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user