test(sha256): add 100k nonce equivalence and hitmask checks
This commit is contained in:
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;
|
||||
}
|
||||
228
tests/test_sha256_backend.c
Normal file
228
tests/test_sha256_backend.c
Normal file
@@ -0,0 +1,228 @@
|
||||
#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_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_hitmask_basic()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("test_sha256_backend: OK\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user