} 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;
/*
/*
* 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
* @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);
* @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.
* @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,
#include <hashx_thread.h>
#include <hashx_time.h>
-typedef struct solver_output {
- equix_solution sols[EQUIX_MAX_SOLS];
- int count;
-} solver_output;
-
typedef struct worker_job {
int id;
hashx_thread thread;
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;
"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) {
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;
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;
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]);
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]);
#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);
- if (ctx->hash_func == NULL) {
- goto failure;
+ 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 == HASHX_NOTSUPP) {
- ctx_failure = EQUIX_NOTSUPP;
+ if (ctx->hash_func == NULL) {
goto failure;
}
+
if (flags & EQUIX_CTX_SOLVE) {
if (flags & EQUIX_CTX_HUGEPAGES) {
ctx->heap = hashx_vm_alloc_huge(sizeof(solver_heap));
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));
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
+#include <assert.h>
#include <equix.h>
#include <hashx.h>
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 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);
}
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);
}
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;
} 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;
}
}
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]);
}
}
}