diff options
-rw-r--r-- | src/core/or/circuituse.c | 23 | ||||
-rw-r--r-- | src/feature/hs/hs_cache.c | 2 | ||||
-rw-r--r-- | src/feature/hs/hs_circuit.c | 1 | ||||
-rw-r--r-- | src/feature/hs/hs_client.c | 130 | ||||
-rw-r--r-- | src/feature/hs/hs_client.h | 2 | ||||
-rw-r--r-- | src/feature/hs/hs_pow.c | 27 | ||||
-rw-r--r-- | src/feature/hs/hs_pow.h | 23 | ||||
-rw-r--r-- | src/test/test_hs_client.c | 2 | ||||
-rw-r--r-- | src/test/test_hs_pow_slow.c | 15 |
9 files changed, 142 insertions, 83 deletions
diff --git a/src/core/or/circuituse.c b/src/core/or/circuituse.c index b78f72e835..ac9005e1d4 100644 --- a/src/core/or/circuituse.c +++ b/src/core/or/circuituse.c @@ -564,14 +564,6 @@ circuit_expire_building(void) continue; } - /* Ignore circuits that are waiting for an introduction to a service with - * PoW enabled, it can take an arbitrary amount of time. They will get - * cleaned up if the SOCKS connection is closed. */ - if (TO_ORIGIN_CIRCUIT(victim)->hs_with_pow_circ && - victim->purpose == CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) { - continue; - } - build_state = TO_ORIGIN_CIRCUIT(victim)->build_state; if (build_state && build_state->onehop_tunnel) cutoff = begindir_cutoff; @@ -2560,6 +2552,11 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, circ->hs_ident = hs_ident_circuit_new(&edge_conn->hs_ident->identity_pk); } + if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { + if (hs_client_setup_intro_circ_auth_key(circ) < 0) { + return 0; + } + } if (circ->base_.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND && circ->base_.state == CIRCUIT_STATE_OPEN) circuit_has_opened(circ); @@ -3012,6 +3009,16 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn) conn, CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT, &introcirc); if (retval < 0) return -1; /* failed */ + if (rendcirc && introcirc) { + /* Let's fill out the hs_ident fully as soon as possible, so that + * unreachability counts can be updated properly even if circuits close + * early. */ + tor_assert_nonfatal(!ed25519_public_key_is_zero( + &introcirc->hs_ident->intro_auth_pk)); + ed25519_pubkey_copy(&rendcirc->hs_ident->intro_auth_pk, + &introcirc->hs_ident->intro_auth_pk); + } + if (retval > 0) { /* one has already sent the intro. keep waiting. */ tor_assert(introcirc); diff --git a/src/feature/hs/hs_cache.c b/src/feature/hs/hs_cache.c index dcca1d7086..0cc7dfd031 100644 --- a/src/feature/hs/hs_cache.c +++ b/src/feature/hs/hs_cache.c @@ -581,6 +581,8 @@ cache_client_intro_state_lookup(const ed25519_public_key_t *service_pk, tor_assert(service_pk); tor_assert(auth_key); + tor_assert_nonfatal(!ed25519_public_key_is_zero(service_pk)); + tor_assert_nonfatal(!ed25519_public_key_is_zero(auth_key)); /* Lookup the intro state cache for this service key. */ cache = digest256map_get(hs_cache_client_intro_state, service_pk->pubkey); diff --git a/src/feature/hs/hs_circuit.c b/src/feature/hs/hs_circuit.c index 92217d760a..d5188b514f 100644 --- a/src/feature/hs/hs_circuit.c +++ b/src/feature/hs/hs_circuit.c @@ -251,6 +251,7 @@ create_intro_circuit_identifier(const hs_service_t *service, ident = hs_ident_circuit_new(&service->keys.identity_pk); ed25519_pubkey_copy(&ident->intro_auth_pk, &ip->auth_key_kp.pubkey); + tor_assert_nonfatal(!ed25519_public_key_is_zero(&ident->intro_auth_pk)); return ident; } diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c index 2a620da953..6303b8fe75 100644 --- a/src/feature/hs/hs_client.c +++ b/src/feature/hs/hs_client.c @@ -549,6 +549,7 @@ find_desc_intro_point_by_ident(const hs_ident_circuit_t *ident, tor_assert(ident); tor_assert(desc); + tor_assert_nonfatal(!ed25519_public_key_is_zero(&ident->intro_auth_pk)); SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points, const hs_desc_intro_point_t *, ip) { @@ -634,15 +635,8 @@ send_introduce1(origin_circuit_t *intro_circ, return -1; /* transient failure */ } - /* Cell has been sent successfully. Copy the introduction point - * authentication and encryption key in the rendezvous circuit identifier so - * we can compute the ntor keys when we receive the RENDEZVOUS2 cell. */ - memcpy(&rend_circ->hs_ident->intro_enc_pk, &ip->enc_key, - sizeof(rend_circ->hs_ident->intro_enc_pk)); - ed25519_pubkey_copy(&rend_circ->hs_ident->intro_auth_pk, - &intro_circ->hs_ident->intro_auth_pk); - - /* Now, we wait for an ACK or NAK on this circuit. */ + /* Cell has been sent successfully. + * Now, we wait for an ACK or NAK on this circuit. */ circuit_change_purpose(TO_CIRCUIT(intro_circ), CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT); /* Set timestamp_dirty, because circuit_expire_building expects it to @@ -657,6 +651,17 @@ send_introduce1(origin_circuit_t *intro_circ, * tackle. If asked for higher, we solve it at this cap. */ #define CLIENT_MAX_POW_EFFORT 10000 +/** Set a client-side minimum effort. If the client is choosing to increase + * effort on retry, it will always pick a value >= this lower limit. */ +#define CLIENT_MIN_RETRY_POW_EFFORT 8 + +/** Client effort will double on every retry until this level is hit */ +#define CLIENT_POW_EFFORT_DOUBLE_UNTIL 1000 + +/** After we reach DOUBLE_UNTIL, client effort is multiplied by this amount + * on every retry until we reach MAX_POW_EFFORT. */ +#define CLIENT_POW_RETRY_MULTIPLIER (1.5f) + /** Send an INTRODUCE1 cell along the intro circuit and populate the rend * circuit identifier with the needed key material for the e2e encryption. * Return 0 on success, -1 if there is a transient error such that an action @@ -731,37 +736,76 @@ consider_sending_introduce1(origin_circuit_t *intro_circ, goto perm_err; } - /* If the descriptor contains PoW parameters then the service is - * expecting a PoW solution in the INTRODUCE cell, which we solve here. */ - if (have_module_pow() && - desc->encrypted_data.pow_params && - desc->encrypted_data.pow_params->suggested_effort > 0) { - log_debug(LD_REND, "PoW params present in descriptor."); + /* Copy the introduction point authentication and encryption key + * in the rendezvous circuit identifier so we can compute the ntor keys + * when we receive the RENDEZVOUS2 cell. */ + memcpy(&rend_circ->hs_ident->intro_enc_pk, &ip->enc_key, + sizeof(rend_circ->hs_ident->intro_enc_pk)); - /* make sure we can't be tricked into hopeless quests */ - if (desc->encrypted_data.pow_params->suggested_effort > - CLIENT_MAX_POW_EFFORT) { + /* Optionally choose to solve a client puzzle for this connection. This + * is only available if we have PoW support at compile time, and if the + * service has provided a PoW seed in its descriptor. The puzzle is enabled + * any time effort is nonzero, which can be recommended by the service or + * self-imposed as a result of previous timeouts. + */ + if (have_module_pow() && desc->encrypted_data.pow_params) { + hs_pow_solver_inputs_t pow_inputs = { + .effort = desc->encrypted_data.pow_params->suggested_effort + }; + memcpy(pow_inputs.seed, desc->encrypted_data.pow_params->seed, + sizeof pow_inputs.seed); + log_debug(LD_REND, "PoW params present in descriptor, suggested_effort=%u", + pow_inputs.effort); + + if (pow_inputs.effort > CLIENT_MAX_POW_EFFORT) { log_notice(LD_REND, "Onion service suggested effort %d which is " "higher than we want to solve. Solving at %d instead.", - desc->encrypted_data.pow_params->suggested_effort, - CLIENT_MAX_POW_EFFORT); - - /* clobber it in-place. hopefully this won't have bad side effects. */ - desc->encrypted_data.pow_params->suggested_effort = - CLIENT_MAX_POW_EFFORT; + pow_inputs.effort, CLIENT_MAX_POW_EFFORT); + pow_inputs.effort = CLIENT_MAX_POW_EFFORT; } - /* send it to the client-side pow cpuworker for solving. */ - intro_circ->hs_currently_solving_pow = 1; - if (0 != hs_pow_queue_work(intro_circ->global_identifier, - rend_circ->global_identifier, - desc->encrypted_data.pow_params)) { - log_debug(LD_REND, "Failed to enqueue PoW request"); + const hs_cache_intro_state_t *state = + hs_cache_client_intro_state_find(&intro_circ->hs_ident->identity_pk, + &intro_circ->hs_ident->intro_auth_pk); + uint32_t unreachable_count = state ? state->unreachable_count : 0; + if (state) { + log_debug(LD_REND, "hs_cache state during PoW consideration, " + "error=%d timed_out=%d unreachable_count=%u", + state->error, state->timed_out, state->unreachable_count); + } + uint64_t new_effort = pow_inputs.effort; + for (unsigned n_retry = 0; n_retry < unreachable_count; n_retry++) { + if (new_effort >= CLIENT_MAX_POW_EFFORT) { + break; + } + if (new_effort < CLIENT_POW_EFFORT_DOUBLE_UNTIL) { + new_effort <<= 1; + } else { + new_effort = (uint64_t) (CLIENT_POW_RETRY_MULTIPLIER * new_effort); + } + new_effort = MAX((uint64_t)CLIENT_MIN_RETRY_POW_EFFORT, new_effort); + new_effort = MIN((uint64_t)CLIENT_MAX_POW_EFFORT, new_effort); + } + if (pow_inputs.effort != (uint32_t)new_effort) { + log_notice(LD_REND, "Increasing PoW effort from %d to %d after intro " + "point unreachable_count=%d", + pow_inputs.effort, (int)new_effort, unreachable_count); + pow_inputs.effort = (uint32_t)new_effort; } - /* can't proceed with the intro1 cell yet, so yield back to the - * main loop */ - goto tran_err; + if (pow_inputs.effort > 0) { + /* send it to the client-side pow cpuworker for solving. */ + intro_circ->hs_currently_solving_pow = 1; + if (hs_pow_queue_work(intro_circ->global_identifier, + rend_circ->global_identifier, + &pow_inputs) != 0) { + log_warn(LD_REND, "Failed to enqueue PoW request"); + } + + /* can't proceed with the intro1 cell yet, so yield back to the + * main loop */ + goto tran_err; + } } /* move on to the next phase: actually try to send it */ @@ -796,8 +840,8 @@ consider_sending_introduce1(origin_circuit_t *intro_circ, * * Return 0 if everything went well, otherwise return -1 in the case of errors. */ -static int -setup_intro_circ_auth_key(origin_circuit_t *circ) +int +hs_client_setup_intro_circ_auth_key(origin_circuit_t *circ) { const hs_descriptor_t *desc; const hs_desc_intro_point_t *ip; @@ -843,13 +887,6 @@ client_intro_circ_has_opened(origin_circuit_t *circ) log_info(LD_REND, "Introduction circuit %u has opened. Attaching streams.", (unsigned int) TO_CIRCUIT(circ)->n_circ_id); - /* This is an introduction circuit so we'll attach the correct - * authentication key to the circuit identifier so it can be identified - * properly later on. */ - if (setup_intro_circ_auth_key(circ) < 0) { - return; - } - connection_ap_attach_pending(1); } @@ -2047,6 +2084,7 @@ hs_client_circuit_cleanup_on_free(const circuit_t *circ) orig_circ = CONST_TO_ORIGIN_CIRCUIT(circ); tor_assert(orig_circ->hs_ident); + const ed25519_public_key_t *intro_pk = &orig_circ->hs_ident->intro_auth_pk; has_timed_out = (circ->marked_for_close_orig_reason == END_CIRC_REASON_TIMEOUT); @@ -2061,22 +2099,22 @@ hs_client_circuit_cleanup_on_free(const circuit_t *circ) safe_str_client(ed25519_fmt(&orig_circ->hs_ident->identity_pk)), safe_str_client(build_state_get_exit_nickname(orig_circ->build_state)), failure); + tor_assert_nonfatal(!ed25519_public_key_is_zero(intro_pk)); hs_cache_client_intro_state_note(&orig_circ->hs_ident->identity_pk, - &orig_circ->hs_ident->intro_auth_pk, - failure); + intro_pk, failure); break; case CIRCUIT_PURPOSE_C_INTRODUCING: if (has_timed_out || !orig_circ->build_state) { break; } + tor_assert_nonfatal(!ed25519_public_key_is_zero(intro_pk)); failure = INTRO_POINT_FAILURE_UNREACHABLE; log_info(LD_REND, "Failed v3 intro circ for service %s to intro point %s " "(while building circuit). Marking as unreachable.", safe_str_client(ed25519_fmt(&orig_circ->hs_ident->identity_pk)), safe_str_client(build_state_get_exit_nickname(orig_circ->build_state))); hs_cache_client_intro_state_note(&orig_circ->hs_ident->identity_pk, - &orig_circ->hs_ident->intro_auth_pk, - failure); + intro_pk, failure); break; default: break; diff --git a/src/feature/hs/hs_client.h b/src/feature/hs/hs_client.h index e87cc00b75..234306a3c3 100644 --- a/src/feature/hs/hs_client.h +++ b/src/feature/hs/hs_client.h @@ -119,6 +119,8 @@ int hs_client_any_intro_points_usable(const ed25519_public_key_t *service_pk, int hs_client_refetch_hsdesc(const ed25519_public_key_t *identity_pk); void hs_client_dir_info_changed(void); +int hs_client_setup_intro_circ_auth_key(origin_circuit_t *circ); + int hs_client_send_introduce1(origin_circuit_t *intro_circ, origin_circuit_t *rend_circ); diff --git a/src/feature/hs/hs_pow.c b/src/feature/hs/hs_pow.c index 199c290004..1f6932a55d 100644 --- a/src/feature/hs/hs_pow.c +++ b/src/feature/hs/hs_pow.c @@ -177,7 +177,7 @@ unpack_equix_solution(const uint8_t *bytes_in, * store the solution in pow_solution_out. Returns 0 on success and -1 * otherwise. Called by a client. */ int -hs_pow_solve(const hs_pow_desc_params_t *pow_params, +hs_pow_solve(const hs_pow_solver_inputs_t *pow_inputs, hs_pow_solution_t *pow_solution_out) { int ret = -1; @@ -185,17 +185,15 @@ hs_pow_solve(const hs_pow_desc_params_t *pow_params, uint8_t *challenge = NULL; equix_ctx *ctx = NULL; - tor_assert(pow_params); + tor_assert(pow_inputs); tor_assert(pow_solution_out); - - /* Select E (just using suggested for now) */ - uint32_t effort = pow_params->suggested_effort; + const uint32_t effort = pow_inputs->effort; /* Generate a random nonce N. */ crypto_rand((char *)nonce, sizeof nonce); /* Build EquiX challenge (C || N || INT_32(E)). */ - challenge = build_equix_challenge(pow_params->seed, nonce, effort); + challenge = build_equix_challenge(pow_inputs->seed, nonce, effort); ctx = build_equix_ctx(EQUIX_CTX_SOLVE); if (!ctx) { @@ -218,7 +216,7 @@ hs_pow_solve(const hs_pow_desc_params_t *pow_params, /* Store the effort E. */ pow_solution_out->effort = effort; /* We only store the first 4 bytes of the seed C. */ - memcpy(pow_solution_out->seed_head, pow_params->seed, + memcpy(pow_solution_out->seed_head, pow_inputs->seed, sizeof(pow_solution_out->seed_head)); /* Store the solution S */ memcpy(&pow_solution_out->equix_solution, sol_bytes, sizeof sol_bytes); @@ -353,8 +351,8 @@ hs_pow_free_service_state(hs_pow_service_state_t *state) */ typedef struct pow_worker_job_t { - /** Input: The pow challenge we need to solve. */ - hs_pow_desc_params_t *pow_params; + /** Inputs for the PoW solver (seed, chosen effort) */ + hs_pow_solver_inputs_t pow_inputs; /** State: we'll look these up to figure out how to proceed after. */ uint32_t intro_circ_identifier; @@ -377,15 +375,15 @@ pow_worker_threadfn(void *state_, void *work_) pow_worker_job_t *job = work_; job->pow_solution_out = tor_malloc_zero(sizeof(hs_pow_solution_t)); - if (hs_pow_solve(job->pow_params, job->pow_solution_out)) { - log_info(LD_REND, "Haven't solved the PoW yet. Returning."); + if (hs_pow_solve(&job->pow_inputs, job->pow_solution_out)) { + log_warn(LD_REND, "Failed to run the proof of work solver"); tor_free(job->pow_solution_out); job->pow_solution_out = NULL; /* how we signal that we came up empty */ return WQ_RPL_REPLY; } /* we have a winner! */ - log_info(LD_REND, "cpuworker pow: we have a winner!"); + log_info(LD_REND, "cpuworker has a proof of work solution"); return WQ_RPL_REPLY; } @@ -397,7 +395,6 @@ pow_worker_job_free(pow_worker_job_t *job) { if (!job) return; - tor_free(job->pow_params); tor_free(job->pow_solution_out); tor_free(job); } @@ -470,14 +467,14 @@ pow_worker_replyfn(void *work_) int hs_pow_queue_work(uint32_t intro_circ_identifier, uint32_t rend_circ_identifier, - const hs_pow_desc_params_t *pow_params) + const hs_pow_solver_inputs_t *pow_inputs) { tor_assert(in_main_thread()); pow_worker_job_t *job = tor_malloc_zero(sizeof(*job)); job->intro_circ_identifier = intro_circ_identifier; job->rend_circ_identifier = rend_circ_identifier; - job->pow_params = tor_memdup(pow_params, sizeof(hs_pow_desc_params_t)); + memcpy(&job->pow_inputs, pow_inputs, sizeof job->pow_inputs); workqueue_entry_t *work; work = cpuworker_queue_work(WQ_PRI_LOW, diff --git a/src/feature/hs/hs_pow.h b/src/feature/hs/hs_pow.h index fe78a48d9a..6e3611be69 100644 --- a/src/feature/hs/hs_pow.h +++ b/src/feature/hs/hs_pow.h @@ -56,6 +56,17 @@ typedef struct hs_pow_desc_params_t { time_t expiration_time; } hs_pow_desc_params_t; +/** The inputs to the PoW solver, derived from the descriptor data and the + * client's per-connection effort choices. */ +typedef struct hs_pow_solver_inputs_t { + /** Seed value from a current descriptor */ + uint8_t seed[HS_POW_SEED_LEN]; + + /** Effort chosen by the client. May be higher or ower than + * suggested_effort in the descriptor. */ + uint32_t effort; +} hs_pow_solver_inputs_t; + /** State and parameters of PoW defenses, stored in the service state. */ typedef struct hs_pow_service_state_t { /* If PoW defenses are enabled this is a priority queue containing acceptable @@ -124,7 +135,7 @@ typedef struct hs_pow_solution_t { #define have_module_pow() (1) /* API */ -int hs_pow_solve(const hs_pow_desc_params_t *pow_params, +int hs_pow_solve(const hs_pow_solver_inputs_t *pow_inputs, hs_pow_solution_t *pow_solution_out); int hs_pow_verify(const hs_pow_service_state_t *pow_state, @@ -135,16 +146,16 @@ void hs_pow_free_service_state(hs_pow_service_state_t *state); int hs_pow_queue_work(uint32_t intro_circ_identifier, uint32_t rend_circ_identifier, - const hs_pow_desc_params_t *pow_params); + const hs_pow_solver_inputs_t *pow_inputs); #else /* !defined(HAVE_MODULE_POW) */ #define have_module_pow() (0) static inline int -hs_pow_solve(const hs_pow_desc_params_t *pow_params, +hs_pow_solve(const hs_pow_solver_inputs_t *pow_inputs, hs_pow_solution_t *pow_solution_out) { - (void)pow_params; + (void)pow_inputs; (void)pow_solution_out; return -1; } @@ -173,11 +184,11 @@ hs_pow_free_service_state(hs_pow_service_state_t *state) static inline int hs_pow_queue_work(uint32_t intro_circ_identifier, uint32_t rend_circ_identifier, - const hs_pow_desc_params_t *pow_params) + const hs_pow_solver_inputs_t *pow_inputs) { (void)intro_circ_identifier; (void)rend_circ_identifier; - (void)pow_params; + (void)pow_inputs; return -1; } diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c index 11a5589d21..f873d90212 100644 --- a/src/test/test_hs_client.c +++ b/src/test/test_hs_client.c @@ -177,6 +177,7 @@ helper_get_circ_and_stream_for_test(origin_circuit_t **circ_out, /* prop224: Setup hs ident on the circuit */ or_circ->hs_ident = hs_ident_circuit_new(&service_pk); + or_circ->hs_ident->intro_auth_pk.pubkey[0] = 42; TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN; @@ -1186,6 +1187,7 @@ test_socks_hs_errors(void *arg) circ->purpose = CIRCUIT_PURPOSE_C_REND_READY; ocirc = TO_ORIGIN_CIRCUIT(circ); ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey); + ocirc->hs_ident->intro_auth_pk.pubkey[0] = 42; ocirc->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); /* Code path will log this exit so build it. */ ocirc->build_state->chosen_exit = extend_info_new("TestNickname", digest, diff --git a/src/test/test_hs_pow_slow.c b/src/test/test_hs_pow_slow.c index fdade2d3fa..e7d1311cee 100644 --- a/src/test/test_hs_pow_slow.c +++ b/src/test/test_hs_pow_slow.c @@ -187,17 +187,16 @@ test_hs_pow_vectors(void *arg) uint8_t rng_bytes[HS_POW_NONCE_LEN]; hs_pow_solution_t output; hs_pow_solution_t solution = { 0 }; - hs_pow_desc_params_t params = { - .type = HS_POW_DESC_V1, - .suggested_effort = vectors[vec_i].effort, + hs_pow_solver_inputs_t input = { + .effort = vectors[vec_i].effort, }; - tt_int_op(strlen(seed_hex), OP_EQ, 2 * sizeof params.seed); + tt_int_op(strlen(seed_hex), OP_EQ, 2 * sizeof input.seed); tt_int_op(strlen(solve_rng_hex), OP_EQ, 2 * sizeof rng_bytes); tt_int_op(strlen(nonce_hex), OP_EQ, 2 * sizeof solution.nonce); tt_int_op(strlen(sol_hex), OP_EQ, 2 * sizeof solution.equix_solution); - tt_int_op(base16_decode((char*)params.seed, HS_POW_SEED_LEN, + tt_int_op(base16_decode((char*)input.seed, HS_POW_SEED_LEN, seed_hex, 2 * HS_POW_SEED_LEN), OP_EQ, HS_POW_SEED_LEN); tt_int_op(base16_decode((char*)rng_bytes, sizeof rng_bytes, @@ -210,11 +209,11 @@ test_hs_pow_vectors(void *arg) sizeof solution.equix_solution, sol_hex, 2 * sizeof solution.equix_solution), OP_EQ, HS_POW_EQX_SOL_LEN); - memcpy(solution.seed_head, params.seed, HS_POW_SEED_HEAD_LEN); + memcpy(solution.seed_head, input.seed, HS_POW_SEED_HEAD_LEN); memset(&output, 0xaa, sizeof output); testing_enable_prefilled_rng(rng_bytes, HS_POW_NONCE_LEN); - tt_int_op(0, OP_EQ, hs_pow_solve(¶ms, &output)); + tt_int_op(0, OP_EQ, hs_pow_solve(&input, &output)); testing_disable_prefilled_rng(); tt_mem_op(solution.seed_head, OP_EQ, output.seed_head, @@ -224,7 +223,7 @@ test_hs_pow_vectors(void *arg) tt_mem_op(&solution.equix_solution, OP_EQ, &output.equix_solution, sizeof output.equix_solution); - tt_int_op(testing_one_hs_pow_solution(&output, params.seed), OP_EQ, 0); + tt_int_op(testing_one_hs_pow_solution(&output, input.seed), OP_EQ, 0); } done: |