diff --git a/src/ext/equix/include/equix.h b/src/ext/equix/include/equix.h index 01ab249437..75b25a4d53 100644 --- a/src/ext/equix/include/equix.h +++ b/src/ext/equix/include/equix.h @@ -30,16 +30,35 @@ typedef struct equix_solution { } equix_solution; /* - * Solution verification results + * Extra informational flags returned by the solver + */ +typedef enum equix_solution_flags { + EQUIX_SOLVER_DID_USE_COMPILER = (1 << 0), +} equix_solution_flags; + +/* + * Fixed size buffer containing up to EQUIX_MAX_SOLS solutions. + */ +typedef struct equix_solutions_buffer { + unsigned count; + equix_solution_flags flags; + equix_solution sols[EQUIX_MAX_SOLS]; +} equix_solutions_buffer; + +/* + * Result type for solve and verify operations */ typedef enum equix_result { EQUIX_OK, /* Solution is valid */ - EQUIX_CHALLENGE, /* The challenge is invalid (the internal hash + EQUIX_FAIL_CHALLENGE, /* The challenge is invalid (the internal hash function doesn't pass validation). */ - EQUIX_ORDER, /* Indices are not in the correct order. */ - EQUIX_PARTIAL_SUM, /* The partial sums of the hash values don't + EQUIX_FAIL_ORDER, /* Indices are not in the correct order. */ + EQUIX_FAIL_PARTIAL_SUM, /* The partial sums of the hash values don't have the required number of trailing zeroes. */ - EQUIX_FINAL_SUM /* The hash values don't sum to zero. */ + EQUIX_FAIL_FINAL_SUM, /* The hash values don't sum to zero. */ + EQUIX_FAIL_COMPILE, /* Can't compile, and no fallback is enabled */ + EQUIX_FAIL_NO_SOLVER, /* Solve requested on a context with no solver */ + EQUIX_FAIL_INTERNAL, /* Internal error (bug) */ } equix_result; /* @@ -49,17 +68,15 @@ typedef struct equix_ctx equix_ctx; /* * Flags for context creation -*/ + */ typedef enum equix_ctx_flags { EQUIX_CTX_VERIFY = 0, /* Context for verification */ EQUIX_CTX_SOLVE = 1, /* Context for solving */ - EQUIX_CTX_COMPILE = 2, /* Compile internal hash function */ - EQUIX_CTX_HUGEPAGES = 4, /* Allocate solver memory using HugePages */ + EQUIX_CTX_MUST_COMPILE = 2, /* Must compile internal hash function */ + EQUIX_CTX_TRY_COMPILE = 4, /* Compile if possible */ + EQUIX_CTX_HUGEPAGES = 8, /* Allocate solver memory using HugePages */ } equix_ctx_flags; -/* Sentinel value used to indicate unsupported type */ -#define EQUIX_NOTSUPP ((equix_ctx*)-1) - #if defined(_WIN32) || defined(__CYGWIN__) #define EQUIX_WIN #endif @@ -93,8 +110,7 @@ extern "C" { * @param flags is the type of context to be created * * @return pointer to a newly created context. Returns NULL on memory - * allocation failure and EQUIX_NOTSUPP if the requested type - * is not supported. + * allocation failure. */ EQUIX_API equix_ctx* equix_alloc(equix_ctx_flags flags); @@ -114,13 +130,17 @@ EQUIX_API void equix_free(equix_ctx* ctx); * @param output pointer to the output array where solutions will be * stored * - * @return the number of solutions found + * @return On success, returns EQUIX_OK and sets output->count to the number + * of solutions found, with the solutions themselves written to the + * output buffer. If the challenge is unusable, returns + * EQUIX_FAIL_CHALLENGE. If the EQUIX_CTX_MUST_COMPILE flag is in use + * and the compiler fails, this can return EQUIX_FAIL_COMPILE. */ -EQUIX_API int equix_solve( +EQUIX_API equix_result equix_solve( equix_ctx* ctx, const void* challenge, size_t challenge_size, - equix_solution output[EQUIX_MAX_SOLS]); + equix_solutions_buffer *output); /* * Verify an Equi-X solution. @@ -130,8 +150,9 @@ EQUIX_API int equix_solve( * @param challenge_size size of the challenge * @param solution pointer to the solution to be verified * - * @return verification result -*/ + * @return Verification result. This can return EQUIX_OK or any of the + * EQUIX_FAIL_* error codes. + */ EQUIX_API equix_result equix_verify( equix_ctx* ctx, const void* challenge, diff --git a/src/ext/equix/src/bench.c b/src/ext/equix/src/bench.c index e5b925c3d2..8d3c82e855 100644 --- a/src/ext/equix/src/bench.c +++ b/src/ext/equix/src/bench.c @@ -10,11 +10,6 @@ #include #include -typedef struct solver_output { - equix_solution sols[EQUIX_MAX_SOLS]; - int count; -} solver_output; - typedef struct worker_job { int id; hashx_thread thread; @@ -23,17 +18,29 @@ typedef struct worker_job { int start; int step; int end; - solver_output* output; + equix_solutions_buffer* output; } worker_job; static hashx_thread_retval worker(void* args) { worker_job* job = (worker_job*)args; job->total_sols = 0; - solver_output* outptr = job->output; + equix_solutions_buffer* outptr = job->output; for (int seed = job->start; seed < job->end; seed += job->step) { - int count = equix_solve(job->ctx, &seed, sizeof(seed), outptr->sols); - outptr->count = count; - job->total_sols += count; + equix_result result = equix_solve(job->ctx, &seed, + sizeof(seed), outptr); + if (result == EQUIX_OK) { + job->total_sols += outptr->count; + } else if (result == EQUIX_FAIL_CHALLENGE) { + outptr->count = 0; + } else if (result == EQUIX_FAIL_COMPILE) { + printf("Error: not supported. Try with --interpret\n"); + exit(1); + break; + } else { + printf("Error: unexpected solve failure (%d)\n", (int)result); + exit(1); + break; + } outptr++; } return HASHX_THREAD_SUCCESS; @@ -54,7 +61,10 @@ static const char* result_names[] = { "Invalid nonce", "Indices out of order", "Nonzero partial sum", - "Nonzero final sum" + "Nonzero final sum", + "HashX compiler failed", + "(Internal) Solver not allocated", + "(Internal error)" }; static void print_help(char* executable) { @@ -85,7 +95,7 @@ int main(int argc, char** argv) { read_int_option("--threads", argc, argv, &threads, 1); equix_ctx_flags flags = EQUIX_CTX_SOLVE; if (!interpret) { - flags |= EQUIX_CTX_COMPILE; + flags |= EQUIX_CTX_MUST_COMPILE; } if (huge_pages) { flags |= EQUIX_CTX_HUGEPAGES; @@ -102,15 +112,11 @@ int main(int argc, char** argv) { printf("Error: memory allocation failure\n"); return 1; } - if (jobs[thd].ctx == EQUIX_NOTSUPP) { - printf("Error: not supported. Try with --interpret\n"); - return 1; - } jobs[thd].id = thd; jobs[thd].start = start + thd; jobs[thd].step = threads; jobs[thd].end = start + nonces; - jobs[thd].output = malloc(sizeof(solver_output) * per_thread); + jobs[thd].output = malloc(sizeof(equix_solutions_buffer) * per_thread); if (jobs[thd].output == NULL) { printf("Error: memory allocation failure\n"); return 1; @@ -141,7 +147,7 @@ int main(int argc, char** argv) { if (print_sols) { for (int thd = 0; thd < threads; ++thd) { worker_job* job = &jobs[thd]; - solver_output* outptr = job->output; + equix_solutions_buffer* outptr = job->output; for (int seed = job->start; seed < job->end; seed += job->step) { for (int sol = 0; sol < outptr->count; ++sol) { print_solution(seed, &outptr->sols[sol]); @@ -153,7 +159,7 @@ int main(int argc, char** argv) { time_start = hashx_time(); for (int thd = 0; thd < threads; ++thd) { worker_job* job = &jobs[thd]; - solver_output* outptr = job->output; + equix_solutions_buffer* outptr = job->output; for (int seed = job->start; seed < job->end; seed += job->step) { for (int sol = 0; sol < outptr->count; ++sol) { equix_result result = equix_verify(job->ctx, &seed, sizeof(seed), &outptr->sols[sol]); diff --git a/src/ext/equix/src/context.c b/src/ext/equix/src/context.c index b0aa2d40e5..28edf5e104 100644 --- a/src/ext/equix/src/context.c +++ b/src/ext/equix/src/context.c @@ -8,21 +8,23 @@ #include "solver_heap.h" equix_ctx* equix_alloc(equix_ctx_flags flags) { - equix_ctx* ctx_failure = NULL; equix_ctx* ctx = malloc(sizeof(equix_ctx)); if (ctx == NULL) { goto failure; } - ctx->flags = flags & EQUIX_CTX_COMPILE; - ctx->hash_func = hashx_alloc(flags & EQUIX_CTX_COMPILE ? - HASHX_COMPILED : HASHX_INTERPRETED); + ctx->flags = (equix_ctx_flags)0; + + if (flags & EQUIX_CTX_MUST_COMPILE) { + ctx->hash_func = hashx_alloc(HASHX_TYPE_COMPILED); + } else if (flags & EQUIX_CTX_TRY_COMPILE) { + ctx->hash_func = hashx_alloc(HASHX_TRY_COMPILE); + } else { + ctx->hash_func = hashx_alloc(HASHX_TYPE_INTERPRETED); + } if (ctx->hash_func == NULL) { goto failure; } - if (ctx->hash_func == HASHX_NOTSUPP) { - ctx_failure = EQUIX_NOTSUPP; - goto failure; - } + if (flags & EQUIX_CTX_SOLVE) { if (flags & EQUIX_CTX_HUGEPAGES) { ctx->heap = hashx_vm_alloc_huge(sizeof(solver_heap)); @@ -33,16 +35,19 @@ equix_ctx* equix_alloc(equix_ctx_flags flags) { if (ctx->heap == NULL) { goto failure; } + } else { + ctx->heap = NULL; } + ctx->flags = flags; return ctx; failure: equix_free(ctx); - return ctx_failure; + return NULL; } void equix_free(equix_ctx* ctx) { - if (ctx != NULL && ctx != EQUIX_NOTSUPP) { + if (ctx != NULL) { if (ctx->flags & EQUIX_CTX_SOLVE) { if (ctx->flags & EQUIX_CTX_HUGEPAGES) { hashx_vm_free(ctx->heap, sizeof(solver_heap)); diff --git a/src/ext/equix/src/equix.c b/src/ext/equix/src/equix.c index 5b314ba6ac..a254261509 100644 --- a/src/ext/equix/src/equix.c +++ b/src/ext/equix/src/equix.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -25,61 +26,89 @@ static bool verify_order(const equix_solution* solution) { static uint64_t sum_pair(hashx_ctx* hash_func, equix_idx left, equix_idx right) { uint8_t hash_left[HASHX_SIZE]; uint8_t hash_right[HASHX_SIZE]; - hashx_exec(hash_func, left, hash_left); - hashx_exec(hash_func, right, hash_right); + hashx_result r_left = hashx_exec(hash_func, left, hash_left); + hashx_result r_right = hashx_exec(hash_func, right, hash_right); + assert(r_left == HASHX_OK && r_right == HASHX_OK); return load64(hash_left) + load64(hash_right); } static equix_result verify_internal(hashx_ctx* hash_func, const equix_solution* solution) { uint64_t pair0 = sum_pair(hash_func, solution->idx[0], solution->idx[1]); if (pair0 & EQUIX_STAGE1_MASK) { - return EQUIX_PARTIAL_SUM; + return EQUIX_FAIL_PARTIAL_SUM; } uint64_t pair1 = sum_pair(hash_func, solution->idx[2], solution->idx[3]); if (pair1 & EQUIX_STAGE1_MASK) { - return EQUIX_PARTIAL_SUM; + return EQUIX_FAIL_PARTIAL_SUM; } uint64_t pair4 = pair0 + pair1; if (pair4 & EQUIX_STAGE2_MASK) { - return EQUIX_PARTIAL_SUM; + return EQUIX_FAIL_PARTIAL_SUM; } uint64_t pair2 = sum_pair(hash_func, solution->idx[4], solution->idx[5]); if (pair2 & EQUIX_STAGE1_MASK) { - return EQUIX_PARTIAL_SUM; + return EQUIX_FAIL_PARTIAL_SUM; } uint64_t pair3 = sum_pair(hash_func, solution->idx[6], solution->idx[7]); if (pair3 & EQUIX_STAGE1_MASK) { - return EQUIX_PARTIAL_SUM; + return EQUIX_FAIL_PARTIAL_SUM; } uint64_t pair5 = pair2 + pair3; if (pair5 & EQUIX_STAGE2_MASK) { - return EQUIX_PARTIAL_SUM; + return EQUIX_FAIL_PARTIAL_SUM; } uint64_t pair6 = pair4 + pair5; if (pair6 & EQUIX_FULL_MASK) { - return EQUIX_FINAL_SUM; + return EQUIX_FAIL_FINAL_SUM; } return EQUIX_OK; } -int equix_solve( +static equix_result equix_hashx_make( + equix_ctx* ctx, + const void* challenge, + size_t challenge_size) +{ + switch (hashx_make(ctx->hash_func, challenge, challenge_size)) { + case HASHX_OK: + return EQUIX_OK; + case HASHX_FAIL_SEED: + return EQUIX_FAIL_CHALLENGE; + case HASHX_FAIL_COMPILE: + return EQUIX_FAIL_COMPILE; + case HASHX_FAIL_UNDEFINED: + case HASHX_FAIL_UNPREPARED: + default: + return EQUIX_FAIL_INTERNAL; + } +} + +equix_result equix_solve( equix_ctx* ctx, const void* challenge, size_t challenge_size, - equix_solution output[EQUIX_MAX_SOLS]) + equix_solutions_buffer *output) { if ((ctx->flags & EQUIX_CTX_SOLVE) == 0) { - return 0; + return EQUIX_FAIL_NO_SOLVER; } - if (!hashx_make(ctx->hash_func, challenge, challenge_size)) { - return 0; + equix_result result = equix_hashx_make(ctx, challenge, challenge_size); + if (result != EQUIX_OK) { + return result; } - return equix_solver_solve(ctx->hash_func, ctx->heap, output); + output->flags = 0; + hashx_type func_type; + if (hashx_query_type(ctx->hash_func, &func_type) == HASHX_OK && + func_type == HASHX_TYPE_COMPILED) { + output->flags |= EQUIX_SOLVER_DID_USE_COMPILER; + } + + output->count = equix_solver_solve(ctx->hash_func, ctx->heap, output->sols); + return EQUIX_OK; } - equix_result equix_verify( equix_ctx* ctx, const void* challenge, @@ -87,10 +116,13 @@ equix_result equix_verify( const equix_solution* solution) { if (!verify_order(solution)) { - return EQUIX_ORDER; + return EQUIX_FAIL_ORDER; } - if (!hashx_make(ctx->hash_func, challenge, challenge_size)) { - return EQUIX_CHALLENGE; + + equix_result result = equix_hashx_make(ctx, challenge, challenge_size); + if (result != EQUIX_OK) { + return result; } + return verify_internal(ctx->hash_func, solution); } diff --git a/src/ext/equix/src/solver.c b/src/ext/equix/src/solver.c index 6824b59cc4..1beda06c74 100644 --- a/src/ext/equix/src/solver.c +++ b/src/ext/equix/src/solver.c @@ -49,7 +49,8 @@ typedef stage3_idx_item s3_idx; static FORCE_INLINE uint64_t hash_value(hashx_ctx* hash_func, equix_idx index) { char hash[HASHX_SIZE]; - hashx_exec(hash_func, index, hash); + hashx_result result = hashx_exec(hash_func, index, hash); + assert(result == HASHX_OK); return load64(hash); } diff --git a/src/ext/equix/src/tests.c b/src/ext/equix/src/tests.c index 63fb5bdb1e..75937a7995 100644 --- a/src/ext/equix/src/tests.c +++ b/src/ext/equix/src/tests.c @@ -13,7 +13,7 @@ typedef bool test_func(); static equix_ctx* ctx = NULL; -static equix_solution solution[EQUIX_MAX_SOLS]; +static equix_solutions_buffer output; static int nonce; static int valid_count = 0; static int test_no = 0; @@ -26,8 +26,8 @@ static int test_no = 0; } while(0) static bool test_alloc() { - ctx = equix_alloc(EQUIX_CTX_SOLVE); - assert(ctx != NULL && ctx != EQUIX_NOTSUPP); + ctx = equix_alloc(EQUIX_CTX_SOLVE | EQUIX_CTX_TRY_COMPILE); + assert(ctx != NULL); return true; } @@ -37,60 +37,65 @@ static bool test_free() { } static bool test_solve() { - int num_solutions = 0; - for (nonce = 0; num_solutions == 0 && nonce < 20; ++nonce) { - num_solutions = equix_solve(ctx, &nonce, sizeof(nonce), solution); + output.count = 0; + for (nonce = 0; output.count == 0 && nonce < 20; ++nonce) { + equix_result result = equix_solve(ctx, &nonce, sizeof(nonce), &output); + assert(result == EQUIX_OK); } --nonce; - assert(num_solutions > 0); + assert(output.count > 0); + assert(output.flags == EQUIX_SOLVER_DID_USE_COMPILER || output.flags == 0); + printf("(using %s HashX) ", + (EQUIX_SOLVER_DID_USE_COMPILER & output.flags) + ? "compiled" : "interpreted"); return true; } static bool test_verify1() { - equix_result result = equix_verify(ctx, &nonce, sizeof(nonce), &solution[0]); + equix_result result = equix_verify(ctx, &nonce, sizeof(nonce), &output.sols[0]); assert(result == EQUIX_OK); return true; } static bool test_verify2() { - SWAP_IDX(solution[0].idx[0], solution[0].idx[1]); - equix_result result = equix_verify(ctx, &nonce, sizeof(nonce), &solution[0]); - assert(result == EQUIX_ORDER); + SWAP_IDX(output.sols[0].idx[0], output.sols[0].idx[1]); + equix_result result = equix_verify(ctx, &nonce, sizeof(nonce), &output.sols[0]); + assert(result == EQUIX_FAIL_ORDER); return true; } static bool test_verify3() { - SWAP_IDX(solution[0].idx[0], solution[0].idx[4]); - SWAP_IDX(solution[0].idx[1], solution[0].idx[5]); - SWAP_IDX(solution[0].idx[2], solution[0].idx[6]); - SWAP_IDX(solution[0].idx[3], solution[0].idx[7]); - equix_result result = equix_verify(ctx, &nonce, sizeof(nonce), &solution[0]); - assert(result == EQUIX_ORDER); - SWAP_IDX(solution[0].idx[0], solution[0].idx[4]); - SWAP_IDX(solution[0].idx[1], solution[0].idx[5]); - SWAP_IDX(solution[0].idx[2], solution[0].idx[6]); - SWAP_IDX(solution[0].idx[3], solution[0].idx[7]); + SWAP_IDX(output.sols[0].idx[0], output.sols[0].idx[4]); + SWAP_IDX(output.sols[0].idx[1], output.sols[0].idx[5]); + SWAP_IDX(output.sols[0].idx[2], output.sols[0].idx[6]); + SWAP_IDX(output.sols[0].idx[3], output.sols[0].idx[7]); + equix_result result = equix_verify(ctx, &nonce, sizeof(nonce), &output.sols[0]); + assert(result == EQUIX_FAIL_ORDER); + SWAP_IDX(output.sols[0].idx[0], output.sols[0].idx[4]); + SWAP_IDX(output.sols[0].idx[1], output.sols[0].idx[5]); + SWAP_IDX(output.sols[0].idx[2], output.sols[0].idx[6]); + SWAP_IDX(output.sols[0].idx[3], output.sols[0].idx[7]); return true; } static bool test_verify4() { - SWAP_IDX(solution[0].idx[1], solution[0].idx[2]); - equix_result result = equix_verify(ctx, &nonce, sizeof(nonce), &solution[0]); - assert(result == EQUIX_PARTIAL_SUM); - SWAP_IDX(solution[0].idx[1], solution[0].idx[2]); + SWAP_IDX(output.sols[0].idx[1], output.sols[0].idx[2]); + equix_result result = equix_verify(ctx, &nonce, sizeof(nonce), &output.sols[0]); + assert(result == EQUIX_FAIL_PARTIAL_SUM); + SWAP_IDX(output.sols[0].idx[1], output.sols[0].idx[2]); return true; } static void permute_idx(int start) { if (start == EQUIX_NUM_IDX - 1) { - equix_result result = equix_verify(ctx, &nonce, sizeof(nonce), &solution[0]); + equix_result result = equix_verify(ctx, &nonce, sizeof(nonce), &output.sols[0]); valid_count += result == EQUIX_OK; } else { for (int i = start; i < EQUIX_NUM_IDX; ++i) { - SWAP_IDX(solution[0].idx[start], solution[0].idx[i]); + SWAP_IDX(output.sols[0].idx[start], output.sols[0].idx[i]); permute_idx(start + 1); - SWAP_IDX(solution[0].idx[start], solution[0].idx[i]); + SWAP_IDX(output.sols[0].idx[start], output.sols[0].idx[i]); } } }