diff --git a/icicle/backend/cpu/CMakeLists.txt b/icicle/backend/cpu/CMakeLists.txt index 8e3d97b72..a018bcd14 100644 --- a/icicle/backend/cpu/CMakeLists.txt +++ b/icicle/backend/cpu/CMakeLists.txt @@ -9,9 +9,9 @@ macro(message) endmacro() FetchContent_Declare( Taskflow - GIT_REPOSITORY https://github.com/taskflow/taskflow.git - GIT_TAG v3.8.0 - GIT_SHALLOW TRUE + GIT_REPOSITORY https://github.com/taskflow/taskflow.git + GIT_TAG v3.8.0 + GIT_SHALLOW TRUE ) # Disable unnecessary components set(TF_BUILD_BENCHMARKS OFF CACHE BOOL "Disable Taskflow benchmarks" FORCE) diff --git a/icicle/backend/cpu/src/hash/cpu_poseidon2.cpp b/icicle/backend/cpu/src/hash/cpu_poseidon2.cpp index 6f5f6ff58..e99184ba8 100644 --- a/icicle/backend/cpu/src/hash/cpu_poseidon2.cpp +++ b/icicle/backend/cpu/src/hash/cpu_poseidon2.cpp @@ -1,8 +1,8 @@ #include "icicle/backend/hash/poseidon2_backend.h" #include "icicle/utils/utils.h" -#include "icicle/fields/field.h" -#include +/// These are pre-calculated constants for different curves +#include "icicle/fields/id.h" #if FIELD_ID == BN254 #include "icicle/hash/poseidon2_constants/constants/bn254_poseidon2.h" using namespace poseidon2_constants_bn254; @@ -33,36 +33,32 @@ using namespace poseidon2_constants_koalabear; #endif namespace icicle { - -#define POSEIDON2_MAX_t 24 - - static unsigned int poseidon2_legal_width[] = {2, 3, 4, 8, 12, 16, 20, 24}; - - static Poseidon2ConstantsOptions - poseidon2_constants[POSEIDON2_MAX_t + 1]; // The size of this array is POSEIDON2_MAX_t + 1 because Poseidon2 max t - // is 24. Only terms in poseidon2_legal_width are filled with data. Rest - // of the terms are not relevant. - static bool s_cpu_backend_poseidon2_constants_initialized = false; - - static eIcicleError init_default_constants() + template + class Poseidon2BackendCPU : public HashBackend { - if (s_cpu_backend_poseidon2_constants_initialized) { return eIcicleError::SUCCESS; } - - unsigned int alpha; - unsigned int partial_rounds; - unsigned int full_rounds; - unsigned int upper_full_rounds; - unsigned int bottom_full_rounds; - const std::string* rounds_constants; - const std::string* mds_matrix; - const std::string* partial_matrix_diagonal; - const std::string* partial_matrix_diagonal_m1; - // At this stage it's still unknown what t and use_domain_tag will be used. - // That's the reason that all the relevant members of the poseidon2_constants array are - // loaded at this stage. - for (int t_idx = 0; t_idx < std::size(poseidon2_legal_width); t_idx++) { - unsigned int T = poseidon2_legal_width[t_idx]; // Single poseidon2 hash width - switch (T) { + public: + Poseidon2BackendCPU(unsigned t, const S* domain_tag) + : HashBackend("Poseidon2-CPU", sizeof(S), sizeof(S) * (nullptr != domain_tag ? t - 1 : t)), + m_domain_tag_value(nullptr != domain_tag ? *domain_tag : S::zero()), m_use_domain_tag(nullptr != domain_tag), + t(t) + { + init_poseidon2_constants( + t, false /* dont use_all_zeroes_padding */, default_hash_config(), &m_poseidon2_constants); + } + + eIcicleError init_poseidon2_constants( + int t, bool use_all_zeroes_padding, const HashConfig& config, Poseidon2ConstantsOptions* poseidon2_constants) + { + unsigned int alpha; + unsigned int partial_rounds; + unsigned int full_rounds; + unsigned int upper_full_rounds; + unsigned int bottom_full_rounds; + const std::string* rounds_constants; + const std::string* mds_matrix; + const std::string* partial_matrix_diagonal; + unsigned int T = t; + switch (t) { case 2: alpha = alpha_2; rounds_constants = rounds_constants_2; @@ -145,341 +141,384 @@ namespace icicle { break; default: ICICLE_LOG_ERROR - << "cpu_poseidon2_init_default_constants: T (width) must be one of [2, 3, 4, 8, 12, 16, 20, 24]\n"; + << "cpu_poseidon2_init_default_constants: t (width) must be one of [2, 3, 4, 8, 12, 16, 20, 24]"; return eIcicleError::INVALID_ARGUMENT; - } - if (full_rounds == 0 && partial_rounds == 0) { // All arrays are empty in this case. - continue; + } // switch (t) { + if (full_rounds == 0 && partial_rounds == 0) { // All arrays are empty in this case (true for wide fields (width > + // 32) & t > 8). + return eIcicleError::SUCCESS; } - scalar_t* h_rounds_constants = new scalar_t[full_rounds * T + partial_rounds]; - for (int i = 0; i < (full_rounds * T + partial_rounds); i++) { - h_rounds_constants[i] = scalar_t::hex_str2scalar(rounds_constants[i]); + scalar_t* scalar_rounds_constants = new scalar_t[full_rounds * t + partial_rounds]; + for (int i = 0; i < (full_rounds * t + partial_rounds); i++) { + scalar_rounds_constants[i] = scalar_t::hex_str2scalar(rounds_constants[i]); } - - scalar_t* h_mds_matrix = new scalar_t[T * T]; - for (int i = 0; i < (T * T); i++) { - h_mds_matrix[i] = scalar_t::hex_str2scalar(mds_matrix[i]); + scalar_t* scalar_mds_matrix = new scalar_t[t * t]; + for (int i = 0; i < (t * t); i++) { + scalar_mds_matrix[i] = scalar_t::hex_str2scalar(mds_matrix[i]); } - - scalar_t* h_partial_matrix_diagonal = new scalar_t[T]; - scalar_t* h_partial_matrix_diagonal_m1 = new scalar_t[T]; - for (int i = 0; i < T; i++) { - h_partial_matrix_diagonal[i] = scalar_t::hex_str2scalar(partial_matrix_diagonal[i]); - h_partial_matrix_diagonal_m1[i] = h_partial_matrix_diagonal[i] - scalar_t::from(1); + scalar_t* scalar_partial_matrix_diagonal = new scalar_t[t]; + scalar_t* scalar_partial_matrix_diagonal_m1 = new scalar_t[t]; + for (int i = 0; i < t; i++) { + scalar_partial_matrix_diagonal[i] = scalar_t::hex_str2scalar(partial_matrix_diagonal[i]); + scalar_partial_matrix_diagonal_m1[i] = scalar_partial_matrix_diagonal[i] - scalar_t::from(1); } - poseidon2_constants[T].t = T; - poseidon2_constants[T].alpha = alpha; - poseidon2_constants[T].nof_upper_full_rounds = upper_full_rounds; - poseidon2_constants[T].nof_bottom_full_rounds = bottom_full_rounds; - poseidon2_constants[T].nof_partial_rounds = partial_rounds; - poseidon2_constants[T].rounds_constants = h_rounds_constants; - poseidon2_constants[T].mds_matrix = h_mds_matrix; - poseidon2_constants[T].partial_matrix_diagonal_m1 = h_partial_matrix_diagonal_m1; - } // for (int t_idx = 0; t_idx < std::size(poseidon2_legal_width); t_idx++) - - s_cpu_backend_poseidon2_constants_initialized = true; - return eIcicleError::SUCCESS; - } // static eIcicleError init_default_constants() + poseidon2_constants->t = t; + poseidon2_constants->alpha = alpha; + poseidon2_constants->use_all_zeroes_padding = use_all_zeroes_padding; + poseidon2_constants->nof_upper_full_rounds = upper_full_rounds; + poseidon2_constants->nof_partial_rounds = partial_rounds; + poseidon2_constants->nof_bottom_full_rounds = bottom_full_rounds; + poseidon2_constants->rounds_constants = scalar_rounds_constants; + poseidon2_constants->mds_matrix = scalar_mds_matrix; + poseidon2_constants->partial_matrix_diagonal_m1 = scalar_partial_matrix_diagonal_m1; - template - class Poseidon2BackendCPU : public HashBackend - { - public: - Poseidon2BackendCPU(unsigned t, const S* domain_tag) - : HashBackend("Poseidon2-CPU", sizeof(S), sizeof(S) * (nullptr != domain_tag ? t - 1 : t)), - m_domain_tag(nullptr != domain_tag ? *domain_tag : S::zero()), m_use_domain_tag(nullptr != domain_tag), m_t(t) - { - init_default_constants(); - } + return eIcicleError::SUCCESS; + } // eIcicleError init_poseidon2_constants( - // For merkle tree size should be equal to the arity of a single hasher multiplier by sizeof(S). - // For sponge function it could be any number. - // Size parameter here is in bytes. + // size - number of bytes in a single inputs. + // The total size of the input should be size * config.batch. eIcicleError hash(const std::byte* input, uint64_t size, const HashConfig& config, std::byte* output) const override { - const unsigned arity = m_use_domain_tag ? m_t - 1 : m_t; - bool is_sponge = false; - int input_size_in_scalars = size / sizeof(S); - if ((config.batch == 1) && (input_size_in_scalars != (m_use_domain_tag ? m_t - 1 : m_t))) { // Check if sponge - // function. - is_sponge = true; - if (config.batch != 1) { - ICICLE_LOG_ERROR << "The only supported value of config.batch for sponge functions is 1.\n"; - return eIcicleError::INVALID_ARGUMENT; - } - } // sponge function - else { // Non-sponge function. - if ((m_use_domain_tag ? input_size_in_scalars : input_size_in_scalars - 1) % (m_t - 1) != 0) { - ICICLE_LOG_ERROR << "Padding isn't supported for non-sponge function hash. The following should be true: " - "((m_use_domain_tag ? size : size-1) % (m_t-1) != 0).\n"; - return eIcicleError::INVALID_ARGUMENT; - } - } // Non-sponge function. - - const unsigned int T = m_t; - bool is_unsupported_T_for_this_field = poseidon2_constants[T].nof_upper_full_rounds == 0; + bool is_unsupported_T_for_this_field = m_poseidon2_constants.nof_upper_full_rounds == 0; if (is_unsupported_T_for_this_field) { - ICICLE_LOG_ERROR << "Unsupported poseidon width (t = " << T << ") for this field! Planned for next version"; + ICICLE_LOG_ERROR << "Unsupported poseidon2 width (t=" << t << ") for this field!"; return eIcicleError::API_NOT_IMPLEMENTED; } - int alpha = poseidon2_constants[T].alpha; - int nof_upper_full_rounds = poseidon2_constants[T].nof_upper_full_rounds; - int nof_partial_rounds = poseidon2_constants[T].nof_partial_rounds; - int nof_bottom_full_rounds = poseidon2_constants[T].nof_bottom_full_rounds; - S* rounds_constants = poseidon2_constants[T].rounds_constants; - S* mds_matrix = poseidon2_constants[T].mds_matrix; - S* partial_matrix_diagonal_m1 = poseidon2_constants[T].partial_matrix_diagonal_m1; - - // Allocate temporary memory for intermediate calcs and in order not to change the input. - // int sponge_nof_hashers = m_use_domain_tag ? (input_size_in_scalars / arity) : ((input_size_in_scalars - 1) / - // (arity - 1)); int tmp_fields_nof_scalars = is_sponge ? (T * sponge_nof_hashers) : (T * config.batch); S* - // tmp_fields = new S[tmp_fields_nof_scalars]; - S* tmp_fields; - S* tmp_fields_init_ptr; // This pointer to keep initial tmp_fields value to perform a easy rollback when needed. - int sponge_nof_hashers; - const S* in_fields = (S*)(input); - int padding_size = 0; - S* padding; + int input_size_in_scalars = size / sizeof(S); + bool is_sponge = input_size_in_scalars != (m_use_domain_tag ? t - 1 : t); + + // Generate padding indications. + int sponge_nof_hashers = 0; + int padding_size_in_scalars = 0; + bool is_padding_needed = false; if (is_sponge) { - if (input_size_in_scalars < T) { // Single hasher in the chain. + if (input_size_in_scalars < t) { // Single hasher in the chain. sponge_nof_hashers = 1; - padding_size = T - (input_size_in_scalars + (m_use_domain_tag == true)); - } else if (input_size_in_scalars >= T) { // More than a single hasher in the chain. - sponge_nof_hashers = (input_size_in_scalars - !(m_use_domain_tag == true) + (T - 2)) / (T - 1); - bool is_padding_needed = (input_size_in_scalars - !(m_use_domain_tag == true)) % (T - 1); + is_padding_needed = true; + padding_size_in_scalars = t - (input_size_in_scalars + (m_use_domain_tag == true)); + } else { // More than a single hasher in the chain. + sponge_nof_hashers = (input_size_in_scalars - !(m_use_domain_tag == true) + (t - 2)) / (t - 1); + is_padding_needed = (input_size_in_scalars - !(m_use_domain_tag == true)) % (t - 1); if (is_padding_needed) { - padding_size = (T - 1) - ((input_size_in_scalars - !(m_use_domain_tag == true)) % (T - 1)); + padding_size_in_scalars = (t - 1) - ((input_size_in_scalars - !(m_use_domain_tag == true)) % (t - 1)); } } - if (padding_size > 0) { // Fill padding array with 1,0,0,... - padding = new S[padding_size]; - padding[0] = S::from(1); - for (int i = 1; i < padding_size; i++) { - padding[i] = S::from(0); - } - } - tmp_fields = new S[T * sponge_nof_hashers]; - tmp_fields_init_ptr = tmp_fields; - // Take care of hasher 0. It's done separately of the rest of the hashers because of the domain tag. - if (m_use_domain_tag) { - // Domain tag exists only for the first hasher. For the rest of the hashers this - // input is undefined at this stage and its value will be set later. - // tmp_fields = {{dt, in0}, {undef, in1}, {undef, in2}, etc.} - memcpy(tmp_fields, &m_domain_tag, sizeof(S)); - } else { - // tmp_fields = {{in0 (T inputs)}, {undef, in1 (T-1 inputs)}, {under, in2 (T-1 inputs)}, etc.} - memcpy(tmp_fields, &in_fields[0], sizeof(S)); - in_fields += 1; - } - tmp_fields += 1; - // Take care of rest of the hashers (T-1 scalar to each hasher). - for (int hasher_idx = 0; hasher_idx < sponge_nof_hashers; hasher_idx++) { - if (hasher_idx == sponge_nof_hashers - 1 && padding_size > 0) { - // Last hasher in the chain. Take care of padding if needed. - memcpy(tmp_fields, in_fields, (T - padding_size - 1) * sizeof(S)); - memcpy(tmp_fields + T - padding_size - 1, padding, padding_size * sizeof(S)); - } else { // Not a last hasher in the chain. There is no padding. - memcpy(tmp_fields, in_fields, (T - 1) * sizeof(S)); - } - in_fields += (T - 1); - tmp_fields += T; - } - tmp_fields = tmp_fields_init_ptr; // Rollback to initial value. } // if (is_sponge) { - else { // Not a sponge function. The is no padding. - // Input of each hash should have domain tag at its input. - // tmp_fields = {{dt, in0 (T-1 inputs)}, {dt, in1 (T-1 inputs)}, {dt, in2 (T-1 inputs)}, etc.} - tmp_fields = new S[T * config.batch]; - tmp_fields_init_ptr = tmp_fields; // Keep tmp_fields pointer for delete. - if (m_use_domain_tag) { - for (int batch_idx = 0; batch_idx < config.batch; batch_idx++) { - memcpy(tmp_fields, &m_domain_tag, sizeof(S)); - memcpy(tmp_fields + 1, in_fields, (T - 1) * sizeof(S)); - in_fields += (T - 1); - tmp_fields += T; - } - tmp_fields = tmp_fields_init_ptr; // Rollback to initial value. - } else { - // tmp_fields = {{in0 (T inputs)}, {in1 (T inputs)}, {in2 (T inputs)}, etc.} - memcpy(tmp_fields, in_fields, T * config.batch * sizeof(S)); - } - } - // Hashes processing. +#define PERMUTATION_SPONGE_T(T) \ + case T: \ + if constexpr (!is_large_field || T <= 8) { \ + permutation_error = poseidon2_sponge_permutation( \ + in_fields, input_size_in_scalars, is_padding_needed, padding_size_in_scalars, m_use_domain_tag, \ + m_domain_tag_value, out, sponge_nof_hashers, config.batch, m_poseidon2_constants); \ + } \ + break; + +#define PERMUTATION_T(T) \ + case T: \ + if constexpr (!is_large_field || T <= 8) { \ + permutation_error = poseidon2_permutation( \ + in_fields, m_use_domain_tag, m_domain_tag_value, out, config.batch, m_poseidon2_constants); \ + } \ + break; + + const S* in_fields = (S*)(input); + S* out = (S*)(output); + constexpr bool is_large_field = sizeof(S) > 8; + eIcicleError permutation_error = eIcicleError::SUCCESS; + if (is_sponge) { - // Call hash_single for hasher[0] - eIcicleError err = hash_single( - tmp_fields /* input */, tmp_fields /* output */, alpha, nof_upper_full_rounds, nof_partial_rounds, - nof_bottom_full_rounds, rounds_constants, mds_matrix, partial_matrix_diagonal_m1); - S* tmp_fields_tmp_ptr = tmp_fields; // Save current pointer in order to access prev output. - if (err != eIcicleError::SUCCESS) return err; - if (sponge_nof_hashers != 1) { - tmp_fields[T] = tmp_fields[0]; // Current first output is an input to the next hasher. + switch (t) { + PERMUTATION_SPONGE_T(2); + PERMUTATION_SPONGE_T(3); + PERMUTATION_SPONGE_T(4); + PERMUTATION_SPONGE_T(8); + PERMUTATION_SPONGE_T(12); + PERMUTATION_SPONGE_T(16); + PERMUTATION_SPONGE_T(20); + PERMUTATION_SPONGE_T(24); } - tmp_fields += T; - // Process rest of the hashers. - for (int hasher_idx = 1; hasher_idx < sponge_nof_hashers; hasher_idx++) { - // The first output of the prev hasher is the first input of the current hasher. - // The T-1 new inputs of the current hasher should be added to the T-1 outputs of the - // prev hasher (starting from index 1). - for (int i = 1; i < T; i++) { - tmp_fields[i] = tmp_fields_tmp_ptr[i] + tmp_fields[i]; - } - eIcicleError err = hash_single( - tmp_fields /* input */, tmp_fields /* output */, alpha, nof_upper_full_rounds, nof_partial_rounds, - nof_bottom_full_rounds, rounds_constants, mds_matrix, partial_matrix_diagonal_m1); - tmp_fields_tmp_ptr = tmp_fields; // Save current pointer in order to access prev output. - if (err != eIcicleError::SUCCESS) return err; - if (hasher_idx != sponge_nof_hashers - 1) // Not to do in the last loop to prevent mem leak. - tmp_fields[T] = tmp_fields[0]; // Fill first scalar of the input to the next hasher. - tmp_fields += T; // Now tmp_fields points to input of the next hasher before the addition. - } // for (int hasher_idx = 1; hasher_idx < sponge_nof_hashers; hasher_idx++) { - tmp_fields -= T; // Rollback to the last hasher output. - memcpy(output, (std::byte*)(&tmp_fields[1]), sizeof(S)); - tmp_fields = tmp_fields_init_ptr; // Rollback to initial value. - } else { // Not a sponge function. - for (int batch_hash_idx = 0; batch_hash_idx < config.batch; batch_hash_idx++) { - eIcicleError err = hash_single( - tmp_fields /* input */, tmp_fields /* output */, alpha, nof_upper_full_rounds, nof_partial_rounds, - nof_bottom_full_rounds, rounds_constants, mds_matrix, partial_matrix_diagonal_m1); - if (err != eIcicleError::SUCCESS) return err; - memcpy(output, (std::byte*)(&tmp_fields[1]), sizeof(S)); - tmp_fields += T; - output += sizeof(S); + } else { + switch (t) { + PERMUTATION_T(2); + PERMUTATION_T(3); + PERMUTATION_T(4); + PERMUTATION_T(8); + PERMUTATION_T(12); + PERMUTATION_T(16); + PERMUTATION_T(20); + PERMUTATION_T(24); } - tmp_fields = tmp_fields_init_ptr; // Rollback to initial value. } - delete[] tmp_fields; - tmp_fields = nullptr; - if (padding_size != 0) { - delete[] padding; - padding = nullptr; - } - - return eIcicleError::SUCCESS; + return permutation_error; } // eIcicleError hash(const std::byte* input, uint64_t size, const HashConfig& config, std::byte* output) const // override private: - // // DEBUG start. Do not remove!!! - // void print_state(std::string str, const S* state_to_print, int count) const { - // std::cout << str << std::endl; - // for (int state_idx = 0; state_idx < count; state_idx++) { // Columns of matrix. - // std::cout << std::hex << state_to_print[state_idx] << std::endl; - // } - // } - // void print_matrix(std::string str, S* matrix_to_print) const { - // std::cout << str << std::endl; - // unsigned int T = m_t; - // for (int matrix_idx = 0; matrix_idx < T*T; matrix_idx++) { // Columns of matrix. - // std::cout << std::hex << matrix_to_print[matrix_idx] << std::endl; - // } - // } - // // DEBUG end - - // This function performs a single hash according to parameters in the poseidon2_constants[] struct. - // eIcicleError hash_single(const std::byte* input, std::byte* output) const - eIcicleError hash_single( - S* tmp_fields, - S* hasher_output, - int alpha, - int nof_upper_full_rounds, - int nof_partial_rounds, - int nof_bottom_full_rounds, - S* rounds_constants, - S* mds_matrix, - S* partial_matrix_diagonal_m1) const + template + void prepare_poseidon2_states(const S* input, S* states, bool use_domain_tag, S domain_tag) const { - const unsigned int T = m_t; - - // Pre-rounds full matrix multiplication. - full_matrix_mul_by_vector(tmp_fields, mds_matrix, tmp_fields); - - // Upper full rounds. - full_rounds(nof_upper_full_rounds, tmp_fields, rounds_constants); - - // Partial rounds. Perform calculation only for the first element of *tmp_fields. - for (int partial_rounds_idx = 0; partial_rounds_idx < nof_partial_rounds; partial_rounds_idx++) { - // Add round constants - tmp_fields[0] = tmp_fields[0] + *rounds_constants++; - // S box - tmp_fields[0] = S::pow(tmp_fields[0], alpha); - // Multiplication by partial (sparse) matrix. - partial_matrix_diagonal_m1_mul_by_vector(tmp_fields, partial_matrix_diagonal_m1, tmp_fields); + S prepared_element; +#pragma unroll + for (int element_idx_in_hash = 0; element_idx_in_hash < T; element_idx_in_hash++) { + if (use_domain_tag) { + if (element_idx_in_hash == 0) { + prepared_element = domain_tag; + } else { + prepared_element = input[element_idx_in_hash - 1]; + } + } else { + prepared_element = input[element_idx_in_hash]; + } + states[element_idx_in_hash] = prepared_element; } - - // Bottom full rounds. - full_rounds(nof_bottom_full_rounds, tmp_fields, rounds_constants); - - memcpy(hasher_output, (std::byte*)(tmp_fields), T * sizeof(S)); - // memcpy(output, (std::byte*)(&tmp_fields[1]), sizeof(S)); - - return eIcicleError::SUCCESS; - } // eIcicleError hash_single(const std::byte* input, std::byte* output) const - - // This function performs a partial_matrix_diagonal_m1 matrix by vector multiplication. - // Note that in order to increase the performance the partial matrix diagonal values - // are actually partial matrix diagonal values minus 1. - void partial_matrix_diagonal_m1_mul_by_vector(const S* vec_in, const S* matrix_in, S* result) const + } // prepare_poseidon2_states(const S* input, S* states, bool use_domain_tag, S domain_tag) + + template + void prepare_poseidon2_sponge_states( + const S*& input, + const bool is_padding_needed, + const S* padding, + const int padding_size_in_scalars, + S* states, + const bool is_last_hasher) const { - unsigned int T = m_t; - S tmp_col_res[T]; // Have to use temp storage because vec_in and result are the same storage. - S vec_in_sum = vec_in[0]; - for (int vec_in_idx = 1; vec_in_idx < T; vec_in_idx++) - vec_in_sum = vec_in_sum + vec_in[vec_in_idx]; - for (int col_idx = 0; col_idx < T; col_idx++) - tmp_col_res[col_idx] = vec_in_sum + matrix_in[col_idx] * vec_in[col_idx]; - for (int col_idx = 0; col_idx < T; col_idx++) { // This copy is needed because vec_in and result storages are - // actually the same storage when calling to the function. - result[col_idx] = tmp_col_res[col_idx]; +#pragma unroll + for (int states_idx = 1; states_idx < T; states_idx++) { + if (is_last_hasher && is_padding_needed) { + int nof_valid_inputs_in_hashers = (T - 1) - padding_size_in_scalars; + if (states_idx < 1 + nof_valid_inputs_in_hashers) + states[states_idx] = states[states_idx] + input[states_idx - 1]; + else + states[states_idx] = states[states_idx] + padding[states_idx - (1 + nof_valid_inputs_in_hashers)]; + } else { // Not last hasher or no padding. + states[states_idx] = states[states_idx] + input[states_idx - 1]; + } } - } // void partial_matrix_diagonal_m1_mul_by_vector(const S* vec_in, const S* matrix_in, S* result) const + } // prepare_poseidon2_sponge_states( // This function performs a full matrix by vector multiplication. - void full_matrix_mul_by_vector(const S* vec_in, const S* matrix_in, S* result) const + template + void full_matrix_mul_vec(const S* vec_in, const S* matrix_in, S* result) const { - unsigned int T = m_t; S tmp_col_res[T]; // Have to use temp storage because vec_in and result are the same storage. +#pragma unroll for (int row_idx = 0; row_idx < T; row_idx++) { // Rows of matrix. tmp_col_res[row_idx] = matrix_in[row_idx * T] * vec_in[0]; +#pragma unroll for (int col_idx = 1; col_idx < T; col_idx++) { // Columns of matrix. tmp_col_res[row_idx] = tmp_col_res[row_idx] + matrix_in[row_idx * T + col_idx] * vec_in[col_idx]; } } +#pragma unroll for (int col_idx = 0; col_idx < T; col_idx++) { // This copy is needed because vec_in and result storages are // actually the same storage when calling to the function. result[col_idx] = tmp_col_res[col_idx]; } } // eIcicleError hash_single(const std::byte* input, std::byte* output) const - // This function performs a needed number of full rounds calculations. - void full_rounds(const unsigned int nof_full_rounds, S* in_out_fields, S*& rounds_constants) const + template + void pre_full_round(S* states, const Poseidon2ConstantsOptions constants) const { - unsigned int T = m_t; - unsigned int alpha = poseidon2_constants[T].alpha; - S* mds_matrix = poseidon2_constants[T].mds_matrix; - for (int full_rounds_idx = 0; full_rounds_idx < nof_full_rounds; full_rounds_idx++) { - for (int state_idx = 0; state_idx < T; state_idx++) { - // Add round constants - in_out_fields[state_idx] = in_out_fields[state_idx] + *rounds_constants++; - } - for (int state_idx = 0; state_idx < T; state_idx++) { - // S box - in_out_fields[state_idx] = S::pow(in_out_fields[state_idx], alpha); - } - // Multiplication by matrix - full_matrix_mul_by_vector(in_out_fields, mds_matrix, in_out_fields); + // Multiply mds matrix by the states vector. + S* matrix = constants.mds_matrix; + full_matrix_mul_vec(states, matrix, states); + } + + template + void full_round(S* states, size_t rc_offset, const S* rounds_constants, const S* mds_matrix, const int alpha) const + { +#pragma unroll + for (int element_idx_in_hash = 0; element_idx_in_hash < T; element_idx_in_hash++) { + states[element_idx_in_hash] = states[element_idx_in_hash] + rounds_constants[rc_offset + element_idx_in_hash]; + } +#pragma unroll + for (int element_idx_in_hash = 0; element_idx_in_hash < T; element_idx_in_hash++) { + states[element_idx_in_hash] = S::pow(states[element_idx_in_hash], alpha); } - } // void full_rounds(const unsigned int nof_full_rounds, S* in_out_fields, S*& rounds_constants) const + full_matrix_mul_vec(states, mds_matrix, states); + } + + template + void full_rounds( + S* states, + size_t rc_offset, + const int nof_upper_full_rounds, + const S* rounds_constants, + const S* mds_matrix, + const int alpha) const + { + for (int i = 0; i < nof_upper_full_rounds; i++) { + full_round(states, rc_offset, rounds_constants, mds_matrix, alpha); + rc_offset += T; + } + } + + template + void partial_round( + S state[T], size_t rc_offset, const S* rounds_constants, const S* partial_matrix_diagonal_m1, const int alpha) + const + { + state[0] = state[0] + rounds_constants[rc_offset]; + + state[0] = S::pow(state[0], alpha); + + // Multiply partial matrix by vector. + // Partial matrix is represented by T members - diagonal members of the matrix. + // The values are actual values minus 1 in order to gain performance. + S vec_in_sum = state[0]; +#pragma unroll + for (int i = 1; i < T; i++) { + vec_in_sum = vec_in_sum + state[i]; + } +#pragma unroll + for (int i = 0; i < T; i++) { + state[i] = vec_in_sum + (partial_matrix_diagonal_m1[i] * state[i]); + } + for (int i = 0; i < T; i++) {} + } // void partial_round(S state[T], size_t rc_offset, const Poseidon2ConstantsOptions& constants) + + template + // void partial_rounds(S* states, size_t rc_offset, const Poseidon2ConstantsOptions constants) const + void partial_rounds( + S* states, + size_t rc_offset, + const S* rounds_constants, + const S* partial_matrix_diagonal_m1, + const int nof_partial_rounds, + const int alpha) const + { + for (int i = 0; i < nof_partial_rounds; i++) { + partial_round(states, rc_offset, rounds_constants, partial_matrix_diagonal_m1, alpha); + rc_offset++; + } + } + + template + void squeeze_states(const S* states, unsigned int offset, S* out) const + { + out[0] = states[offset]; + } + + template + eIcicleError poseidon2_permutation( + const S* input, + bool use_domain_tag, + const S domain_tag_value, + S* out, + unsigned int batch_size, + const Poseidon2ConstantsOptions& constants) const + { + S* states = new S[T]; + + for (int batch_idx = 0; batch_idx < batch_size; batch_idx++) { + prepare_poseidon2_states(input, states, use_domain_tag, domain_tag_value); + + pre_full_round(states, constants); + + size_t rc_offset = 0; + + full_rounds( + states, rc_offset, constants.nof_upper_full_rounds, constants.rounds_constants, constants.mds_matrix, + constants.alpha); + rc_offset += T * constants.nof_upper_full_rounds; + + partial_rounds( + states, rc_offset, constants.rounds_constants, constants.partial_matrix_diagonal_m1, + constants.nof_partial_rounds, constants.alpha); + rc_offset += constants.nof_partial_rounds; + + full_rounds( + states, rc_offset, constants.nof_upper_full_rounds, constants.rounds_constants, constants.mds_matrix, + constants.alpha); + + out[0] = states[1]; + + input += use_domain_tag ? T - 1 : T; // Move to the next hasher input. + out += 1; // Move to the output of the next hasher. + } // for (int batch_idx = 0; batch_idx < batch_size; batch_idx++) { + + return eIcicleError::SUCCESS; + } // eIcicleError poseidon2_permutation() + + template + eIcicleError poseidon2_sponge_permutation( + const S* input, + const int input_size_in_scalars, + const bool is_padding_needed, + const int padding_size_in_scalars, + const bool use_domain_tag, + const S domain_tag_value, + S* out, + int nof_hashers, + unsigned int batch_size, + const Poseidon2ConstantsOptions& constants) const + { + S padding[T]; + padding[0] = S::from(1); + for (int i = 1; i < T; i++) + padding[i] = S::from(0); + + for (int batch_idx = 0; batch_idx < batch_size; batch_idx++) { + const S* input_shadow = input; + + // states vector should be set to zero in order to use addition in prepare_poseidon2_sponge_states. + S states[T]; + for (int i = 0; i < T; i++) + states[i] = S::from(0); + // Take care of first input of the first hasher. Rest of the input has T-1 granularity. + if (use_domain_tag) { + states[0] = domain_tag_value; + } else { + states[0] = input[0]; + input += 1; + } + + for (int hasher_idx = 0; hasher_idx < nof_hashers; hasher_idx++) { + bool is_last_hasher = hasher_idx == nof_hashers - 1; + + prepare_poseidon2_sponge_states( + input, is_padding_needed, padding, padding_size_in_scalars, states, is_last_hasher); + input += T - 1; // Move to the next hasher. + + pre_full_round(states, constants); + + size_t rc_offset = 0; + + full_rounds( + states, rc_offset, constants.nof_upper_full_rounds, constants.rounds_constants, constants.mds_matrix, + constants.alpha); + rc_offset += T * constants.nof_upper_full_rounds; + + partial_rounds( + states, rc_offset, constants.rounds_constants, constants.partial_matrix_diagonal_m1, + constants.nof_partial_rounds, constants.alpha); + rc_offset += constants.nof_partial_rounds; + + full_rounds( + states, rc_offset, constants.nof_upper_full_rounds, constants.rounds_constants, constants.mds_matrix, + constants.alpha); + + } // for (int hasher_idx = 0; hasher_idx < nof_hashers; hasher_idx ++) { + + out[batch_idx] = states[1]; + input = input_shadow + input_size_in_scalars; // Move to the input of the next hasher. + } // for (int batch_idx = 0; batch_idx < batch_size; batch_idx++) { + + return eIcicleError::SUCCESS; + } // eIcicleError poseidon2_sponge_permutation( + + const unsigned t; + Poseidon2ConstantsOptions m_poseidon2_constants; const bool m_use_domain_tag; - const S m_domain_tag; - const unsigned int m_t; - }; // class Poseidon2BackendCPU : public HashBackend + const S m_domain_tag_value; + + }; // class Poseidon2BackendCUDA : public HashBackend static eIcicleError create_cpu_poseidon2_hash_backend( const Device& device, unsigned t, const scalar_t* domain_tag, std::shared_ptr& backend /*OUT*/) @@ -490,4 +529,4 @@ namespace icicle { REGISTER_CREATE_POSEIDON2_BACKEND("CPU", create_cpu_poseidon2_hash_backend); -} // namespace icicle \ No newline at end of file +} // namespace icicle diff --git a/icicle/tests/test_hash_api.cpp b/icicle/tests/test_hash_api.cpp index 52e920896..7c8c803ed 100644 --- a/icicle/tests/test_hash_api.cpp +++ b/icicle/tests/test_hash_api.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "test_base.h" #include "icicle/utils/rand_gen.h" @@ -1053,7 +1054,6 @@ TEST_F(HashApiTest, poseidon_3_batch_without_dt) scalar_t::rand_host_many(input.get(), t * config.batch); auto run = [&](const std::string& dev_type, scalar_t* out, bool measure, const char* msg, int iters) { - std::cout << "iters = " << iters << std::endl; Device dev = {dev_type, 0}; icicle_set_device(dev); @@ -1149,8 +1149,11 @@ TEST_F(HashApiTest, poseidon_tree) #endif // POSEIDON +//////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////// + #ifdef POSEIDON2 - // DEBUG. This test could run only with bn254 curve. Dont remove!!! // bn254: p = 0x303b6f7c86d043bfcbcc80214f26a30277a15d3f74ca654992defe7ff8d03570 #include "icicle/fields/field_config.h" using namespace field_config; @@ -1161,13 +1164,7 @@ TEST_F(HashApiTest, poseidon2_3_single_hasher) config.batch = 1; auto input = std::make_unique(config.batch * t); - // for (int i = 0; i < config.batch * t; i++) { - // input[i] = scalar_t::from(i % t); - // } scalar_t::rand_host_many(input.get(), t * config.batch); - // for (int i = 0; i < config.batch * t; i++) { - // std::cout << "poseidon2_3_single_hasher input " << input[i] << std::endl; - // } auto run = [&](const std::string& dev_type, scalar_t* out, bool measure, const char* msg, int iters) { Device dev = {dev_type, 0}; @@ -1185,44 +1182,26 @@ TEST_F(HashApiTest, poseidon2_3_single_hasher) END_TIMER(POSEIDON2_sync, oss.str().c_str(), measure); }; - std::cout << "Run CPU only test " << std::endl; auto output_cpu = std::make_unique(config.batch); run(IcicleTestBase::reference_device(), output_cpu.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); - // #if FIELD_ID == BN254 - // scalar_t expected_res = - // scalar_t::hex_str2scalar("0x303b6f7c86d043bfcbcc80214f26a30277a15d3f74ca654992defe7ff8d03570"); - // ASSERT_EQ(expected_res, *(output_cpu.get())); - // #endif if (IcicleTestBase::main_device() == "CUDA") { - std::cout << "Run CUDA test " << std::endl; auto output_mainDev = std::make_unique(config.batch); run(IcicleTestBase::main_device(), output_mainDev.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); ASSERT_EQ(0, memcmp(output_cpu.get(), output_mainDev.get(), config.batch * sizeof(scalar_t))); - std::cout << "End CUDA test " << std::endl; } } // poseidon2_3_single_hasher -// Test used to generate expected result of any hasher ccording to the parameters inside. -TEST_F(HashApiTest, poseidon2_3_gen_hasher_expected_result_cpu_only) +// Sponge, chain of 2 hashers, no padding, no domain tag. +TEST_F(HashApiTest, poseidon2_4_sponge_2_hashers_without_dt) { - #if FIELD_ID == BN254 - const unsigned t = 3; + const unsigned t = 4; auto config = default_hash_config(); - config.batch = 1; + int nof_hashers = 2; + int nof_inputs = 1 + nof_hashers * (t - 1); - auto input = std::make_unique(t); - input[0] = scalar_t::hex_str2scalar("0x00e6997915531bf1cdb5d90acacc4a14d0960a3de8ac56b520de319b627db7bf"); - // input[1] = scalar_t::hex_str2scalar("0x17e49ca947671143af1f5038d0ed615d4db68cb2b475b00fb253a1f0886a80c0"); - // input[2] = scalar_t::hex_str2scalar("0x0001e7319635c0fdeabf4dbc8c539c296682a0c9260a8f457b3565e49299d308"); - // input[3] = scalar_t::hex_str2scalar("0x1aa6c65a496292747d2667b5e72a3521637b4d4d8dbc99d0b555696c1cdb9a34"); - // input[0] = scalar_t::from(4); - input[1] = scalar_t::from(7); - input[2] = scalar_t::from(8); - // input[3] = scalar_t::from(7); - // for (int i = 0; i < t; i++) { - // std::cout << "poseidon2_3_gen_hasher_expected_result_cpu_only input " << input[i] << std::endl; - // } + auto input = std::make_unique(nof_inputs); + scalar_t::rand_host_many(input.get(), nof_inputs); auto run = [&](const std::string& dev_type, scalar_t* out, bool measure, const char* msg, int iters) { Device dev = {dev_type, 0}; @@ -1235,37 +1214,41 @@ TEST_F(HashApiTest, poseidon2_3_gen_hasher_expected_result_cpu_only) START_TIMER(POSEIDON2_sync) for (int i = 0; i < iters; ++i) { - ICICLE_CHECK(poseidon2.hash(input.get(), t, config, out)); + ICICLE_CHECK(poseidon2.hash(input.get(), nof_inputs, config, out)); } END_TIMER(POSEIDON2_sync, oss.str().c_str(), measure); }; - auto output_cpu = std::make_unique(config.batch); + auto output_cpu = std::make_unique(config.batch); // config.batch = 1 here. run(IcicleTestBase::reference_device(), output_cpu.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); - #endif -} // poseidon2_3_gen_hasher_expected_result_cpu_only -// Sponge, chain of 2 hashers, no padding, no domain tag. -TEST_F(HashApiTest, poseidon2_3_sponge_2_hashers_without_dt) + std::string main_device = IcicleTestBase::main_device(); + if (IcicleTestBase::main_device() == "CUDA") { + auto output_mainDev = std::make_unique(config.batch); + run(IcicleTestBase::main_device(), output_mainDev.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); + // std::cout << "Output CPU = " << output_cpu[0] << std::endl; + // std::cout << "Output CUDA = " << output_mainDev[0] << std::endl; + ASSERT_EQ(0, memcmp(output_cpu.get(), output_mainDev.get(), config.batch * sizeof(scalar_t))); + } +} // poseidon2_4_sponge_2_hashers_without_dt + +// Test used to generate expected result of any hasher ccording to the parameters inside. +TEST_F(HashApiTest, poseidon2_3_gen_hasher_expected_result_cpu_only) { - const unsigned t = 3; auto config = default_hash_config(); - int nof_hashers = 2; - int nof_inputs = 1 + nof_hashers * (t - 1); + config.batch = 1; - auto input = std::make_unique(nof_inputs); - // for (int i = 0; i < t; i++) { // First hasher has t inputs. - // input[i] = scalar_t::from(i); - // } - // for (int hasher_idx = 1; hasher_idx < nof_hashers; hasher_idx++) { - // for (int i = 1; i < t; i++) { // Non-first hashers have t-1 inputs. - // input[hasher_idx * (t - 1) + i] = scalar_t::from(i); - // } - // } - scalar_t::rand_host_many(input.get(), nof_inputs); - // for (int i = 0; i < t + (nof_hashers-1) * (t-1); i++) { - // std::cout << "poseidon2_3_sponge_2_hashers_without_dt input " << input[i] << std::endl; - // } + const unsigned t = 4; + auto input = std::make_unique(t); + // Set the inputs as needed. + for (int i = 0; i < config.batch; i++) { + for (int j = 0; j < t; j++) { + input[i * t + j] = scalar_t::from(j + 4); + } + } + for (int i = 0; i < config.batch * t; i++) { + std::cout << "Input = " << input[i] << std::endl; + } auto run = [&](const std::string& dev_type, scalar_t* out, bool measure, const char* msg, int iters) { Device dev = {dev_type, 0}; @@ -1278,49 +1261,153 @@ TEST_F(HashApiTest, poseidon2_3_sponge_2_hashers_without_dt) START_TIMER(POSEIDON2_sync) for (int i = 0; i < iters; ++i) { - ICICLE_CHECK(poseidon2.hash(input.get(), t + (nof_hashers - 1) * (t - 1), config, out)); + ICICLE_CHECK(poseidon2.hash(input.get(), t, config, out)); } END_TIMER(POSEIDON2_sync, oss.str().c_str(), measure); - }; + }; // - std::cout << "Run CPU test " << std::endl; - auto output_cpu = std::make_unique(config.batch); // config.batch = 1 here. + auto output_cpu = std::make_unique(config.batch); run(IcicleTestBase::reference_device(), output_cpu.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); - // #if FIELD_ID == BN254 - // scalar_t expected_res = - // scalar_t::hex_str2scalar("0x0d54b4b71781dc4f30afe3a90f76559379f6f75aea6968d4c986512e2711ad20"); - // ASSERT_EQ(expected_res, *(output_cpu.get())); - // #endif - - std::string main_device = IcicleTestBase::main_device(); - if (IcicleTestBase::main_device() == "CUDA") { - std::cout << "Run CUDA test " << std::endl; - auto output_mainDev = std::make_unique(config.batch); - run(IcicleTestBase::main_device(), output_mainDev.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); - ASSERT_EQ(0, memcmp(output_cpu.get(), output_mainDev.get(), config.batch * sizeof(scalar_t))); + for (int i = 0; i < config.batch; i++) { + std::cout << "Output = " << output_cpu[i] << std::endl; } -} // poseidon2_3_sponge_2_hashers_without_dt +} // poseidon2_3_gen_hasher_expected_result_cpu_only + +TEST_F(HashApiTest, poseidon2_non_sponge_all_included_test) +{ + scalar_t domain_tag; + std::unique_ptr outputs[2]; // Used to compare CPU and CUDA results. + + for (auto t : {2, 3, 4, 8, 12, 16, 20, 24}) { + if (scalar_t::TLC > 1 && t > 4) continue; + auto config = default_hash_config(); + for (auto batch_size : {1, 16, 256}) { + config.batch = batch_size; + outputs[0] = std::make_unique(batch_size); + outputs[1] = std::make_unique(batch_size); + for (auto use_domain_tag : {false, true}) { + std::unique_ptr input; + if (use_domain_tag) { + input = std::make_unique(config.batch * (t - 1)); + domain_tag = scalar_t::rand_host(); + scalar_t::rand_host_many(input.get(), (t - 1) * config.batch); + } // No domain tag. + else { + input = std::make_unique(config.batch * t); + scalar_t::rand_host_many(input.get(), t * config.batch); + } + for (const auto& device : s_registered_devices) { + icicle_set_device(device); + auto out = std::make_unique(config.batch); + std::ostringstream oss; + oss << std::string(device) << " " << "poseidon2"; + if (use_domain_tag) { + auto poseidon2 = Poseidon2::create(t, &domain_tag); + START_TIMER(POSEIDON2_sync) + for (int i = 0; i < ITERS; ++i) { + ICICLE_CHECK(poseidon2.hash(input.get(), t - 1, config, out.get())); + } + END_TIMER(POSEIDON2_sync, oss.str().c_str(), VERBOSE); + } else { // No domain tag. + auto poseidon2 = Poseidon2::create(t); + START_TIMER(POSEIDON2_sync) + for (int i = 0; i < ITERS; ++i) { + ICICLE_CHECK(poseidon2.hash(input.get(), t, config, out.get())); + } + END_TIMER(POSEIDON2_sync, oss.str().c_str(), VERBOSE); + } + if (std::string(device) == "CUDA") { + outputs[0] = std::move(out); + } else { + outputs[1] = std::move(out); + } + } // for (const auto& device : s_registered_devices) { + // for (int i=0; i(t, &domain_tag); + START_TIMER(POSEIDON2_sync) + std::cout << "use_domain_tag, Device = " << device << std::endl; + for (int i = 0; i < ITERS; ++i) { + ICICLE_CHECK(poseidon2.hash(input.get(), nof_hashers * (t - 1) - padding_size, config, out.get())); + } + END_TIMER(POSEIDON2_sync, oss.str().c_str(), VERBOSE); + } // No domain tag. + else { + auto poseidon2 = Poseidon2::create(t); + START_TIMER(POSEIDON2_sync) + std::cout << "Not use_domain_tag, Device = " << device << std::endl; + for (int i = 0; i < ITERS; ++i) { + ICICLE_CHECK( + poseidon2.hash(input.get(), 1 + nof_hashers * (t - 1) - padding_size, config, out.get())); + } + END_TIMER(POSEIDON2_sync, oss.str().c_str(), VERBOSE); + } // Domain tag. + if (std::string(device) == "CUDA") + outputs[0] = std::move(out); + else + outputs[1] = std::move(out); + } // for (const auto& device : s_registered_devices) { + // for (int i=0; i(nof_valid_inputs); - // for (int i = 0; i < nof_valid_inputs; i++) { // First hasher has t inputs. - // input[i] = scalar_t::from(5 + i); - // } scalar_t::rand_host_many(input.get(), nof_valid_inputs); - // for (int i = 0; i < nof_valid_inputs; i++) { - // std::cout << "poseidon2_3_sponge_2_hashers_with_dt input " << input[i] << std::endl; - // } auto run = [&](const std::string& dev_type, scalar_t* out, bool measure, const char* msg, int iters) { Device dev = {dev_type, 0}; @@ -1338,25 +1425,20 @@ TEST_F(HashApiTest, poseidon2_3_sponge_2_hashers_with_dt) END_TIMER(POSEIDON2_sync, oss.str().c_str(), measure); }; - std::cout << "Run CPU test " << std::endl; auto output_cpu = std::make_unique(config.batch); // config.batch = 1 here. run(IcicleTestBase::reference_device(), output_cpu.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); - // #if FIELD_ID == BN254 - // scalar_t expected_res = - // scalar_t::hex_str2scalar("0x286786cd695dbe838456c72a5edb4d2717a17a0971006671c26c0219c3de5abe"); - // ASSERT_EQ(expected_res, *(output_cpu.get())); - // #endif if (IcicleTestBase::main_device() == "CUDA") { - std::cout << "Run CUDA test " << std::endl; auto output_mainDev = std::make_unique(config.batch); run(IcicleTestBase::main_device(), output_mainDev.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); + // std::cout << "CPU output = " << output_cpu[0] << std::endl; + // std::cout << "CUDA output = " << output_mainDev[0] << std::endl; ASSERT_EQ(0, memcmp(output_cpu.get(), output_mainDev.get(), config.batch * sizeof(scalar_t))); } } // poseidon2_3_sponge_2_hashers_with_dt // Sponge, chain of 1 hasher, with padding (2 scalars 1,0), no domain tag. -TEST_F(HashApiTest, poseidon2_3_sponge_1_hasher_without_dt_with_padding) +TEST_F(HashApiTest, poseidon2_4_sponge_1_hasher_without_dt_with_padding) { const unsigned t = 4; auto config = default_hash_config(); @@ -1364,13 +1446,7 @@ TEST_F(HashApiTest, poseidon2_3_sponge_1_hasher_without_dt_with_padding) int nof_valid_inputs = 2; auto input = std::make_unique(nof_valid_inputs); - // for (int i = 0; i < nof_valid_inputs; i++) { // First hasher has t inputs. - // input[i] = scalar_t::from(4 + i); // Valid inputs are 4,5 - // } scalar_t::rand_host_many(input.get(), nof_valid_inputs); - // for (int i = 0; i < nof_valid_inputs; i++) { - // std::cout << "poseidon2_3_sponge_1_hasher_without_dt_with_padding input " << input[i] << std::endl; - // } auto run = [&](const std::string& dev_type, scalar_t* out, bool measure, const char* msg, int iters) { Device dev = {dev_type, 0}; @@ -1380,7 +1456,7 @@ TEST_F(HashApiTest, poseidon2_3_sponge_1_hasher_without_dt_with_padding) oss << dev_type << " " << msg; auto poseidon2 = Poseidon2::create(t); - + std::cout << "Device = " << dev << std::endl; START_TIMER(POSEIDON2_sync) for (int i = 0; i < iters; ++i) { ICICLE_CHECK(poseidon2.hash(input.get(), nof_valid_inputs, config, out)); @@ -1388,39 +1464,28 @@ TEST_F(HashApiTest, poseidon2_3_sponge_1_hasher_without_dt_with_padding) END_TIMER(POSEIDON2_sync, oss.str().c_str(), measure); }; - std::cout << "Run CPU test " << std::endl; auto output_cpu = std::make_unique(config.batch); // config.batch = 1 here. run(IcicleTestBase::reference_device(), output_cpu.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); - // #if FIELD_ID == BN254 - // scalar_t expected_res = - // scalar_t::hex_str2scalar("0x2645a6e432f38bb4f197b1b4a69d6fac977cdb4927cfbb62f133a9e427dee146"); - // ASSERT_EQ(expected_res, *(output_cpu.get())); - // #endif if (IcicleTestBase::main_device() == "CUDA") { - std::cout << "Run CUDA test " << std::endl; auto output_mainDev = std::make_unique(config.batch); run(IcicleTestBase::main_device(), output_mainDev.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); + // std::cout << "CPU output = " << output_cpu[0] << std::endl; + // std::cout << "CUDA output = " << output_mainDev[0] << std::endl; ASSERT_EQ(0, memcmp(output_cpu.get(), output_mainDev.get(), config.batch * sizeof(scalar_t))); } -} // poseidon2_3_sponge_1_hasher_without_dt_with_padding +} // poseidon2_4_sponge_1_hasher_without_dt_with_padding -// Sponge, chain of 2 hashers, with padding (2 scalars 1,0), no domain tag. -TEST_F(HashApiTest, poseidon2_4_sponge_2_hashers_without_dt_with_padding) +// Sponge, chain of 1 hasher, with padding (2 scalars 1,0), no domain tag. +TEST_F(HashApiTest, poseidon2_3_sponge_1_hasher_without_dt_with_padding_debug) { - const unsigned t = 4; + const unsigned t = 3; auto config = default_hash_config(); - int nof_hashers = 2; - int nof_valid_inputs = 5; + int nof_hashers = 1; + int nof_valid_inputs = 2; auto input = std::make_unique(nof_valid_inputs); - // for (int i = 0; i < nof_valid_inputs; i++) { // First hasher has t inputs. - // input[i] = scalar_t::from(4 + i); // Valid inputs are 4,5 - // } scalar_t::rand_host_many(input.get(), nof_valid_inputs); - // for (int i = 0; i < nof_valid_inputs; i++) { - // std::cout << "poseidon2_4_sponge_2_hashers_without_dt_with_padding input " << input[i] << std::endl; - // } auto run = [&](const std::string& dev_type, scalar_t* out, bool measure, const char* msg, int iters) { Device dev = {dev_type, 0}; @@ -1438,46 +1503,28 @@ TEST_F(HashApiTest, poseidon2_4_sponge_2_hashers_without_dt_with_padding) END_TIMER(POSEIDON2_sync, oss.str().c_str(), measure); }; - std::cout << "Run CPU test " << std::endl; auto output_cpu = std::make_unique(config.batch); // config.batch = 1 here. run(IcicleTestBase::reference_device(), output_cpu.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); - // #if FIELD_ID == BN254 - // scalar_t expected_res = - // scalar_t::hex_str2scalar("0x0353fae9638ef80c9a80786ce24c42d6e087695a40cd5ca29207f20b72f25379"); - // ASSERT_EQ(expected_res, *(output_cpu.get())); - // #endif if (IcicleTestBase::main_device() == "CUDA") { - std::cout << "Run CUDA test " << std::endl; auto output_mainDev = std::make_unique(config.batch); run(IcicleTestBase::main_device(), output_mainDev.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); ASSERT_EQ(0, memcmp(output_cpu.get(), output_mainDev.get(), config.batch * sizeof(scalar_t))); } -} // poseidon2_4_sponge_2_hashers_without_dt_with_padding +} // poseidon2_3_sponge_1_hasher_without_dt_with_padding_debug -// Test check single hash without domain tag. -TEST_F(HashApiTest, poseidon2_3_single_hash_without_dt) +// Sponge, chain of 2 hashers, with padding (2 scalars 1,0), no domain tag. +TEST_F(HashApiTest, poseidon2_4_sponge_2_hashers_without_dt_with_padding) { - const unsigned t = 3; + const unsigned t = 4; auto config = default_hash_config(); - config.batch = 1; + int nof_hashers = 2; + int nof_valid_inputs = 5; - auto input = std::make_unique(t * config.batch); - scalar_t::rand_host_many(input.get(), t * config.batch); - // // DEBUG - sets known inputs. Do not remove!!! - // input[0] = scalar_t::hex_str2scalar("0x0bb61d24daca55eebcb1929a82650f328134334da98ea4f847f760054f4a3033"); - // input[1] = scalar_t::hex_str2scalar("0x303b6f7c86d043bfcbcc80214f26a30277a15d3f74ca654992defe7ff8d03571"); - // input[2] = scalar_t::hex_str2scalar("0x1ed25194542b12eef8617361c3ba7c52e660b145994427cc86296242cf766eca"); - // // for (int i=1; i(nof_valid_inputs); + scalar_t::rand_host_many(input.get(), nof_valid_inputs); auto run = [&](const std::string& dev_type, scalar_t* out, bool measure, const char* msg, int iters) { - std::cout << "iters = " << iters << std::endl; Device dev = {dev_type, 0}; icicle_set_device(dev); @@ -1488,42 +1535,30 @@ TEST_F(HashApiTest, poseidon2_3_single_hash_without_dt) START_TIMER(POSEIDON2_sync) for (int i = 0; i < iters; ++i) { - ICICLE_CHECK(poseidon2.hash(input.get(), t, config, out)); + ICICLE_CHECK(poseidon2.hash(input.get(), nof_valid_inputs, config, out)); } END_TIMER(POSEIDON2_sync, oss.str().c_str(), measure); }; - auto output_cpu = std::make_unique(config.batch); - auto output_mainDev = std::make_unique(config.batch); - + auto output_cpu = std::make_unique(config.batch); // config.batch = 1 here. run(IcicleTestBase::reference_device(), output_cpu.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); - run(IcicleTestBase::main_device(), output_mainDev.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); - ASSERT_EQ(0, memcmp(output_cpu.get(), output_mainDev.get(), config.batch * sizeof(scalar_t))); -} // poseidon2_3_single_hash_without_dt + if (IcicleTestBase::main_device() == "CUDA") { + auto output_mainDev = std::make_unique(config.batch); + run(IcicleTestBase::main_device(), output_mainDev.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); + ASSERT_EQ(0, memcmp(output_cpu.get(), output_mainDev.get(), config.batch * sizeof(scalar_t))); + } +} // poseidon2_4_sponge_2_hashers_without_dt_with_padding -TEST_F(HashApiTest, poseidon2_3_sponge_hash_2_without_dt) +// Test check single hash without domain tag. +TEST_F(HashApiTest, poseidon2_3_single_hash_without_dt) { const unsigned t = 3; auto config = default_hash_config(); - int nof_hashers = 2; - int nof_valid_inputs = 5; + config.batch = 1; - auto input = std::make_unique(nof_valid_inputs); - scalar_t::rand_host_many(input.get(), nof_valid_inputs); - // // DEBUG - sets known inputs. Do not remove!!! - // for (int i=0; i(t * config.batch); + scalar_t::rand_host_many(input.get(), t * config.batch); auto run = [&](const std::string& dev_type, scalar_t* out, bool measure, const char* msg, int iters) { Device dev = {dev_type, 0}; @@ -1536,22 +1571,21 @@ TEST_F(HashApiTest, poseidon2_3_sponge_hash_2_without_dt) START_TIMER(POSEIDON2_sync) for (int i = 0; i < iters; ++i) { - ICICLE_CHECK(poseidon2.hash(input.get(), 1 + nof_hashers * (t - 1), config, out)); + ICICLE_CHECK(poseidon2.hash(input.get(), t, config, out)); } END_TIMER(POSEIDON2_sync, oss.str().c_str(), measure); }; - auto output_cpu = std::make_unique(1); - auto output_mainDev = std::make_unique(1); + auto output_cpu = std::make_unique(config.batch); + auto output_mainDev = std::make_unique(config.batch); run(IcicleTestBase::reference_device(), output_cpu.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); run(IcicleTestBase::main_device(), output_mainDev.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); ASSERT_EQ(0, memcmp(output_cpu.get(), output_mainDev.get(), config.batch * sizeof(scalar_t))); - int a = 5; -} // poseidon2_3_sponge_hash_2_without_dt +} // poseidon2_3_single_hash_without_dt -TEST_F(HashApiTest, poseidon2_3_sponge_hash_1K_without_dt) +TEST_F(HashApiTest, poseidon2_3_sponge_1K_hashers_without_dt) { const unsigned t = 3; auto config = default_hash_config(); @@ -1560,19 +1594,6 @@ TEST_F(HashApiTest, poseidon2_3_sponge_hash_1K_without_dt) auto input = std::make_unique(nof_valid_inputs); scalar_t::rand_host_many(input.get(), nof_valid_inputs); - // // DEBUG - sets known inputs. Do not remove!!! - // for (int i=0; i(t); - ICICLE_CHECK(poseidon2.hash(input.get(), 1 + nof_hashers * (t - 1), config, out)); START_TIMER(POSEIDON2_sync) for (int i = 0; i < iters; ++i) { - ICICLE_CHECK(poseidon2.hash(input.get(), 1 + nof_hashers * (t - 1), config, out)); + ICICLE_CHECK(poseidon2.hash(input.get(), nof_valid_inputs, config, out)); } END_TIMER(POSEIDON2_sync, oss.str().c_str(), measure); }; @@ -1598,13 +1618,12 @@ TEST_F(HashApiTest, poseidon2_3_sponge_hash_1K_without_dt) run(IcicleTestBase::main_device(), output_mainDev.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); ASSERT_EQ(0, memcmp(output_cpu.get(), output_mainDev.get(), config.batch * sizeof(scalar_t))); - int a = 5; -} // poseidon2_3_sponge_hash_1K_without_dt +} // poseidon2_3_sponge_1K_hashers_without_dt TEST_F(HashApiTest, poseidon2_invalid_t) { - // Large fields do not support some t's at this moment. - // This is testing that a correct error is returned for invalid t + // Large fields do not support some t's. + // This is testing that a correct error is returned for invalid t. const unsigned t = 20; auto config = default_hash_config(); @@ -1625,9 +1644,8 @@ TEST_F(HashApiTest, poseidon2_invalid_t) EXPECT_EQ(err, eIcicleError::SUCCESS); } } -} +} // poseidon2_invalid_t -// Currently there is no support for batch > 1 and domain_tag != nullpotr. TEST_F(HashApiTest, poseidon2_3_single_hash_with_dt) { const unsigned t = 3; @@ -1639,7 +1657,6 @@ TEST_F(HashApiTest, poseidon2_3_single_hash_with_dt) scalar_t::rand_host_many(input.get(), (t - 1) * config.batch); auto run = [&](const std::string& dev_type, scalar_t* out, bool measure, const char* msg, int iters) { - std::cout << "iters = " << iters << std::endl; Device dev = {dev_type, 0}; icicle_set_device(dev); @@ -1664,25 +1681,51 @@ TEST_F(HashApiTest, poseidon2_3_single_hash_with_dt) ASSERT_EQ(0, memcmp(output_cpu.get(), output_mainDev.get(), config.batch * sizeof(scalar_t))); } -TEST_F(HashApiTest, poseidon2_3_batch_without_dt_debug) +TEST_F(HashApiTest, poseidon2_2_batch_16_with_dt) +{ + const unsigned t = 2; + auto config = default_hash_config(); + const scalar_t domain_tag = scalar_t::from(0); + config.batch = 1 << 4; + + auto input = std::make_unique((t - 1) * config.batch); + scalar_t::rand_host_many(input.get(), (t - 1) * config.batch); + + auto run = [&](const std::string& dev_type, scalar_t* out, bool measure, const char* msg, int iters) { + Device dev = {dev_type, 0}; + icicle_set_device(dev); + + std::ostringstream oss; + oss << dev_type << " " << msg; + + auto poseidon2 = Poseidon2::create(t, &domain_tag); + + START_TIMER(POSEIDON2_sync) + for (int i = 0; i < iters; ++i) { + ICICLE_CHECK(poseidon2.hash(input.get(), t - 1, config, out)); + } + END_TIMER(POSEIDON2_sync, oss.str().c_str(), measure); + }; + + auto output_cpu = std::make_unique(config.batch); + auto output_mainDev = std::make_unique(config.batch); + + run(IcicleTestBase::reference_device(), output_cpu.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); + run(IcicleTestBase::main_device(), output_mainDev.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); + + ASSERT_EQ(0, memcmp(output_cpu.get(), output_mainDev.get(), config.batch * sizeof(scalar_t))); +} // poseidon2_2_batch_16_with_dt + +TEST_F(HashApiTest, poseidon2_3_large_batch_without_dt_debug) { const unsigned t = 3; auto config = default_hash_config(); - config.batch = 1 << 10; + config.batch = 1 << 14; auto input = std::make_unique(t * config.batch); scalar_t::rand_host_many(input.get(), t * config.batch); - // // DEBUG - sets known inputs. Do not remove!!! - // for (int i=0; i(t * config.batch); scalar_t::rand_host_many(input.get(), t * config.batch); auto run = [&](const std::string& dev_type, scalar_t* out, bool measure, const char* msg, int iters) { - std::cout << "iters = " << iters << std::endl; Device dev = {dev_type, 0}; icicle_set_device(dev); @@ -1741,10 +1782,14 @@ TEST_F(HashApiTest, poseidon2_3_batch_without_dt) run(IcicleTestBase::main_device(), output_mainDev.get(), VERBOSE /*=measure*/, "poseidon2", ITERS); ASSERT_EQ(0, memcmp(output_cpu.get(), output_mainDev.get(), config.batch * sizeof(scalar_t))); -} +} // poseidon2_3_1K_batch_without_dt #endif // POSEIDON2 +//////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////// + #ifdef SUMCHECK /*======================= (TODO??) Move to Sumcheck test-suite =======================*/ #include "icicle/sumcheck/sumcheck_transcript_config.h" diff --git a/wrappers/golang/curves/bls12377/tests/poseidon2_test.go b/wrappers/golang/curves/bls12377/tests/poseidon2_test.go index decb28dee..23b45d6ef 100644 --- a/wrappers/golang/curves/bls12377/tests/poseidon2_test.go +++ b/wrappers/golang/curves/bls12377/tests/poseidon2_test.go @@ -61,39 +61,42 @@ func testPoseidon2Hash(s *suite.Suite) { } } -// TODO uncomment once Poseidon2 sponge function is ready -// func testPoseidon2HashSponge(s *suite.Suite) { -// for _, t := range []int{2, 3, 4, 8, 12, 16, 20, 24} { -// inputs := bls12_377.GenerateScalars(t*8 - 2) -// var noDomainTag *bls12_377.ScalarField -// -// // Set device to CPU -// test_helpers.ActivateReferenceDevice() -// outputsRef := make([]bls12_377.ScalarField, 1) -// poseidon2HasherRef, _ := poseidon2.NewHasher(uint64(t), noDomainTag) -// err := poseidon2HasherRef.Hash( -// core.HostSliceFromElements(inputs), -// core.HostSliceFromElements(outputsRef), -// core.GetDefaultHashConfig(), -// ) -// -// poseidonHasherRef.Delete() -// -// // Set device to main -// test_helpers.ActivateMainDevice() -// outputsMain := make([]bls12_377.ScalarField, 1) -// poseidonHasherMain, _ := poseidon.NewHasher(uint64(t), noDomainTag) -// poseidonHasherMain.Hash( -// core.HostSliceFromElements(inputs), -// core.HostSliceFromElements(outputsMain), -// core.GetDefaultHashConfig(), -// ) -// -// poseidon2HasherMain.Delete() -// -// s.Equal(runtime.InvalidArgument, err) -// } -// } +func testPoseidon2HashSponge(s *suite.Suite) { + for _, t := range []int{2, 3, 4, 8, 12, 16, 20, 24} { + // TODO Danny add 8, 12, 16, 20, 24 for large fields once all is supported + var largeField bls12_377.ScalarField + if largeField.Size() > 4 && t > 4 { + continue // TODO Danny remove this + } + inputs := bls12_377.GenerateScalars(t*8 - 2) + + // Set device to CPU + test_helpers.ActivateReferenceDevice() + outputsRef := make([]bls12_377.ScalarField, 1) + poseidon2HasherRef, _ := poseidon2.NewHasher(uint64(t), nil /*domain tag*/) + poseidon2HasherRef.Hash( + core.HostSliceFromElements(inputs), + core.HostSliceFromElements(outputsRef), + core.GetDefaultHashConfig(), + ) + + poseidon2HasherRef.Delete() + + // Set device to main + test_helpers.ActivateMainDevice() + outputsMain := make([]bls12_377.ScalarField, 1) + poseidon2HasherMain, _ := poseidon2.NewHasher(uint64(t), nil /*domain tag*/) + poseidon2HasherMain.Hash( + core.HostSliceFromElements(inputs), + core.HostSliceFromElements(outputsMain), + core.GetDefaultHashConfig(), + ) + + poseidon2HasherMain.Delete() + + s.Equal(outputsRef, outputsMain, "Poseidon2 hash outputs did not match") + } +} func testPoseidon2HashTree(s *suite.Suite) { t := 4 @@ -132,7 +135,7 @@ type Poseidon2TestSuite struct { func (s *Poseidon2TestSuite) TestPoseidon2() { s.Run("TestPoseidon2Hash", testWrapper(&s.Suite, testPoseidon2Hash)) - // s.Run("TestPoseidonHash2Sponge", testWrapper(&s.Suite, testPoseidonHash2Sponge)) + s.Run("TestPoseidon2HashSponge", testWrapper(&s.Suite, testPoseidon2HashSponge)) s.Run("TestPoseidon2HashTree", testWrapper(&s.Suite, testPoseidon2HashTree)) } diff --git a/wrappers/golang/curves/bls12381/tests/poseidon2_test.go b/wrappers/golang/curves/bls12381/tests/poseidon2_test.go index 4db3b73c8..83514a942 100644 --- a/wrappers/golang/curves/bls12381/tests/poseidon2_test.go +++ b/wrappers/golang/curves/bls12381/tests/poseidon2_test.go @@ -61,39 +61,42 @@ func testPoseidon2Hash(s *suite.Suite) { } } -// TODO uncomment once Poseidon2 sponge function is ready -// func testPoseidon2HashSponge(s *suite.Suite) { -// for _, t := range []int{2, 3, 4, 8, 12, 16, 20, 24} { -// inputs := bls12_381.GenerateScalars(t*8 - 2) -// var noDomainTag *bls12_381.ScalarField -// -// // Set device to CPU -// test_helpers.ActivateReferenceDevice() -// outputsRef := make([]bls12_381.ScalarField, 1) -// poseidon2HasherRef, _ := poseidon2.NewHasher(uint64(t), noDomainTag) -// err := poseidon2HasherRef.Hash( -// core.HostSliceFromElements(inputs), -// core.HostSliceFromElements(outputsRef), -// core.GetDefaultHashConfig(), -// ) -// -// poseidonHasherRef.Delete() -// -// // Set device to main -// test_helpers.ActivateMainDevice() -// outputsMain := make([]bls12_381.ScalarField, 1) -// poseidonHasherMain, _ := poseidon.NewHasher(uint64(t), noDomainTag) -// poseidonHasherMain.Hash( -// core.HostSliceFromElements(inputs), -// core.HostSliceFromElements(outputsMain), -// core.GetDefaultHashConfig(), -// ) -// -// poseidon2HasherMain.Delete() -// -// s.Equal(runtime.InvalidArgument, err) -// } -// } +func testPoseidon2HashSponge(s *suite.Suite) { + for _, t := range []int{2, 3, 4, 8, 12, 16, 20, 24} { + // TODO Danny add 8, 12, 16, 20, 24 for large fields once all is supported + var largeField bls12_381.ScalarField + if largeField.Size() > 4 && t > 4 { + continue // TODO Danny remove this + } + inputs := bls12_381.GenerateScalars(t*8 - 2) + + // Set device to CPU + test_helpers.ActivateReferenceDevice() + outputsRef := make([]bls12_381.ScalarField, 1) + poseidon2HasherRef, _ := poseidon2.NewHasher(uint64(t), nil /*domain tag*/) + poseidon2HasherRef.Hash( + core.HostSliceFromElements(inputs), + core.HostSliceFromElements(outputsRef), + core.GetDefaultHashConfig(), + ) + + poseidon2HasherRef.Delete() + + // Set device to main + test_helpers.ActivateMainDevice() + outputsMain := make([]bls12_381.ScalarField, 1) + poseidon2HasherMain, _ := poseidon2.NewHasher(uint64(t), nil /*domain tag*/) + poseidon2HasherMain.Hash( + core.HostSliceFromElements(inputs), + core.HostSliceFromElements(outputsMain), + core.GetDefaultHashConfig(), + ) + + poseidon2HasherMain.Delete() + + s.Equal(outputsRef, outputsMain, "Poseidon2 hash outputs did not match") + } +} func testPoseidon2HashTree(s *suite.Suite) { t := 4 @@ -132,7 +135,7 @@ type Poseidon2TestSuite struct { func (s *Poseidon2TestSuite) TestPoseidon2() { s.Run("TestPoseidon2Hash", testWrapper(&s.Suite, testPoseidon2Hash)) - // s.Run("TestPoseidonHash2Sponge", testWrapper(&s.Suite, testPoseidonHash2Sponge)) + s.Run("TestPoseidon2HashSponge", testWrapper(&s.Suite, testPoseidon2HashSponge)) s.Run("TestPoseidon2HashTree", testWrapper(&s.Suite, testPoseidon2HashTree)) } diff --git a/wrappers/golang/curves/bn254/tests/poseidon2_test.go b/wrappers/golang/curves/bn254/tests/poseidon2_test.go index 6c0f0b263..c2178f68b 100644 --- a/wrappers/golang/curves/bn254/tests/poseidon2_test.go +++ b/wrappers/golang/curves/bn254/tests/poseidon2_test.go @@ -61,39 +61,42 @@ func testPoseidon2Hash(s *suite.Suite) { } } -// TODO uncomment once Poseidon2 sponge function is ready -// func testPoseidon2HashSponge(s *suite.Suite) { -// for _, t := range []int{2, 3, 4, 8, 12, 16, 20, 24} { -// inputs := bn254.GenerateScalars(t*8 - 2) -// var noDomainTag *bn254.ScalarField -// -// // Set device to CPU -// test_helpers.ActivateReferenceDevice() -// outputsRef := make([]bn254.ScalarField, 1) -// poseidon2HasherRef, _ := poseidon2.NewHasher(uint64(t), noDomainTag) -// err := poseidon2HasherRef.Hash( -// core.HostSliceFromElements(inputs), -// core.HostSliceFromElements(outputsRef), -// core.GetDefaultHashConfig(), -// ) -// -// poseidonHasherRef.Delete() -// -// // Set device to main -// test_helpers.ActivateMainDevice() -// outputsMain := make([]bn254.ScalarField, 1) -// poseidonHasherMain, _ := poseidon.NewHasher(uint64(t), noDomainTag) -// poseidonHasherMain.Hash( -// core.HostSliceFromElements(inputs), -// core.HostSliceFromElements(outputsMain), -// core.GetDefaultHashConfig(), -// ) -// -// poseidon2HasherMain.Delete() -// -// s.Equal(runtime.InvalidArgument, err) -// } -// } +func testPoseidon2HashSponge(s *suite.Suite) { + for _, t := range []int{2, 3, 4, 8, 12, 16, 20, 24} { + // TODO Danny add 8, 12, 16, 20, 24 for large fields once all is supported + var largeField bn254.ScalarField + if largeField.Size() > 4 && t > 4 { + continue // TODO Danny remove this + } + inputs := bn254.GenerateScalars(t*8 - 2) + + // Set device to CPU + test_helpers.ActivateReferenceDevice() + outputsRef := make([]bn254.ScalarField, 1) + poseidon2HasherRef, _ := poseidon2.NewHasher(uint64(t), nil /*domain tag*/) + poseidon2HasherRef.Hash( + core.HostSliceFromElements(inputs), + core.HostSliceFromElements(outputsRef), + core.GetDefaultHashConfig(), + ) + + poseidon2HasherRef.Delete() + + // Set device to main + test_helpers.ActivateMainDevice() + outputsMain := make([]bn254.ScalarField, 1) + poseidon2HasherMain, _ := poseidon2.NewHasher(uint64(t), nil /*domain tag*/) + poseidon2HasherMain.Hash( + core.HostSliceFromElements(inputs), + core.HostSliceFromElements(outputsMain), + core.GetDefaultHashConfig(), + ) + + poseidon2HasherMain.Delete() + + s.Equal(outputsRef, outputsMain, "Poseidon2 hash outputs did not match") + } +} func testPoseidon2HashTree(s *suite.Suite) { t := 4 @@ -132,7 +135,7 @@ type Poseidon2TestSuite struct { func (s *Poseidon2TestSuite) TestPoseidon2() { s.Run("TestPoseidon2Hash", testWrapper(&s.Suite, testPoseidon2Hash)) - // s.Run("TestPoseidonHash2Sponge", testWrapper(&s.Suite, testPoseidonHash2Sponge)) + s.Run("TestPoseidon2HashSponge", testWrapper(&s.Suite, testPoseidon2HashSponge)) s.Run("TestPoseidon2HashTree", testWrapper(&s.Suite, testPoseidon2HashTree)) } diff --git a/wrappers/golang/curves/bw6761/tests/poseidon2_test.go b/wrappers/golang/curves/bw6761/tests/poseidon2_test.go index 5d5bb7e52..e37d41b6a 100644 --- a/wrappers/golang/curves/bw6761/tests/poseidon2_test.go +++ b/wrappers/golang/curves/bw6761/tests/poseidon2_test.go @@ -61,39 +61,42 @@ func testPoseidon2Hash(s *suite.Suite) { } } -// TODO uncomment once Poseidon2 sponge function is ready -// func testPoseidon2HashSponge(s *suite.Suite) { -// for _, t := range []int{2, 3, 4, 8, 12, 16, 20, 24} { -// inputs := bw6_761.GenerateScalars(t*8 - 2) -// var noDomainTag *bw6_761.ScalarField -// -// // Set device to CPU -// test_helpers.ActivateReferenceDevice() -// outputsRef := make([]bw6_761.ScalarField, 1) -// poseidon2HasherRef, _ := poseidon2.NewHasher(uint64(t), noDomainTag) -// err := poseidon2HasherRef.Hash( -// core.HostSliceFromElements(inputs), -// core.HostSliceFromElements(outputsRef), -// core.GetDefaultHashConfig(), -// ) -// -// poseidonHasherRef.Delete() -// -// // Set device to main -// test_helpers.ActivateMainDevice() -// outputsMain := make([]bw6_761.ScalarField, 1) -// poseidonHasherMain, _ := poseidon.NewHasher(uint64(t), noDomainTag) -// poseidonHasherMain.Hash( -// core.HostSliceFromElements(inputs), -// core.HostSliceFromElements(outputsMain), -// core.GetDefaultHashConfig(), -// ) -// -// poseidon2HasherMain.Delete() -// -// s.Equal(runtime.InvalidArgument, err) -// } -// } +func testPoseidon2HashSponge(s *suite.Suite) { + for _, t := range []int{2, 3, 4, 8, 12, 16, 20, 24} { + // TODO Danny add 8, 12, 16, 20, 24 for large fields once all is supported + var largeField bw6_761.ScalarField + if largeField.Size() > 4 && t > 4 { + continue // TODO Danny remove this + } + inputs := bw6_761.GenerateScalars(t*8 - 2) + + // Set device to CPU + test_helpers.ActivateReferenceDevice() + outputsRef := make([]bw6_761.ScalarField, 1) + poseidon2HasherRef, _ := poseidon2.NewHasher(uint64(t), nil /*domain tag*/) + poseidon2HasherRef.Hash( + core.HostSliceFromElements(inputs), + core.HostSliceFromElements(outputsRef), + core.GetDefaultHashConfig(), + ) + + poseidon2HasherRef.Delete() + + // Set device to main + test_helpers.ActivateMainDevice() + outputsMain := make([]bw6_761.ScalarField, 1) + poseidon2HasherMain, _ := poseidon2.NewHasher(uint64(t), nil /*domain tag*/) + poseidon2HasherMain.Hash( + core.HostSliceFromElements(inputs), + core.HostSliceFromElements(outputsMain), + core.GetDefaultHashConfig(), + ) + + poseidon2HasherMain.Delete() + + s.Equal(outputsRef, outputsMain, "Poseidon2 hash outputs did not match") + } +} func testPoseidon2HashTree(s *suite.Suite) { t := 4 @@ -132,7 +135,7 @@ type Poseidon2TestSuite struct { func (s *Poseidon2TestSuite) TestPoseidon2() { s.Run("TestPoseidon2Hash", testWrapper(&s.Suite, testPoseidon2Hash)) - // s.Run("TestPoseidonHash2Sponge", testWrapper(&s.Suite, testPoseidonHash2Sponge)) + s.Run("TestPoseidon2HashSponge", testWrapper(&s.Suite, testPoseidon2HashSponge)) s.Run("TestPoseidon2HashTree", testWrapper(&s.Suite, testPoseidon2HashTree)) } diff --git a/wrappers/golang/curves/grumpkin/tests/poseidon2_test.go b/wrappers/golang/curves/grumpkin/tests/poseidon2_test.go index 1a88ab08e..8bc15547c 100644 --- a/wrappers/golang/curves/grumpkin/tests/poseidon2_test.go +++ b/wrappers/golang/curves/grumpkin/tests/poseidon2_test.go @@ -61,39 +61,42 @@ func testPoseidon2Hash(s *suite.Suite) { } } -// TODO uncomment once Poseidon2 sponge function is ready -// func testPoseidon2HashSponge(s *suite.Suite) { -// for _, t := range []int{2, 3, 4, 8, 12, 16, 20, 24} { -// inputs := grumpkin.GenerateScalars(t*8 - 2) -// var noDomainTag *grumpkin.ScalarField -// -// // Set device to CPU -// test_helpers.ActivateReferenceDevice() -// outputsRef := make([]grumpkin.ScalarField, 1) -// poseidon2HasherRef, _ := poseidon2.NewHasher(uint64(t), noDomainTag) -// err := poseidon2HasherRef.Hash( -// core.HostSliceFromElements(inputs), -// core.HostSliceFromElements(outputsRef), -// core.GetDefaultHashConfig(), -// ) -// -// poseidonHasherRef.Delete() -// -// // Set device to main -// test_helpers.ActivateMainDevice() -// outputsMain := make([]grumpkin.ScalarField, 1) -// poseidonHasherMain, _ := poseidon.NewHasher(uint64(t), noDomainTag) -// poseidonHasherMain.Hash( -// core.HostSliceFromElements(inputs), -// core.HostSliceFromElements(outputsMain), -// core.GetDefaultHashConfig(), -// ) -// -// poseidon2HasherMain.Delete() -// -// s.Equal(runtime.InvalidArgument, err) -// } -// } +func testPoseidon2HashSponge(s *suite.Suite) { + for _, t := range []int{2, 3, 4, 8, 12, 16, 20, 24} { + // TODO Danny add 8, 12, 16, 20, 24 for large fields once all is supported + var largeField grumpkin.ScalarField + if largeField.Size() > 4 && t > 4 { + continue // TODO Danny remove this + } + inputs := grumpkin.GenerateScalars(t*8 - 2) + + // Set device to CPU + test_helpers.ActivateReferenceDevice() + outputsRef := make([]grumpkin.ScalarField, 1) + poseidon2HasherRef, _ := poseidon2.NewHasher(uint64(t), nil /*domain tag*/) + poseidon2HasherRef.Hash( + core.HostSliceFromElements(inputs), + core.HostSliceFromElements(outputsRef), + core.GetDefaultHashConfig(), + ) + + poseidon2HasherRef.Delete() + + // Set device to main + test_helpers.ActivateMainDevice() + outputsMain := make([]grumpkin.ScalarField, 1) + poseidon2HasherMain, _ := poseidon2.NewHasher(uint64(t), nil /*domain tag*/) + poseidon2HasherMain.Hash( + core.HostSliceFromElements(inputs), + core.HostSliceFromElements(outputsMain), + core.GetDefaultHashConfig(), + ) + + poseidon2HasherMain.Delete() + + s.Equal(outputsRef, outputsMain, "Poseidon2 hash outputs did not match") + } +} func testPoseidon2HashTree(s *suite.Suite) { t := 4 @@ -132,7 +135,7 @@ type Poseidon2TestSuite struct { func (s *Poseidon2TestSuite) TestPoseidon2() { s.Run("TestPoseidon2Hash", testWrapper(&s.Suite, testPoseidon2Hash)) - // s.Run("TestPoseidonHash2Sponge", testWrapper(&s.Suite, testPoseidonHash2Sponge)) + s.Run("TestPoseidon2HashSponge", testWrapper(&s.Suite, testPoseidon2HashSponge)) s.Run("TestPoseidon2HashTree", testWrapper(&s.Suite, testPoseidon2HashTree)) } diff --git a/wrappers/golang/fields/babybear/tests/poseidon2_test.go b/wrappers/golang/fields/babybear/tests/poseidon2_test.go index 536e21b67..4a3a84b7f 100644 --- a/wrappers/golang/fields/babybear/tests/poseidon2_test.go +++ b/wrappers/golang/fields/babybear/tests/poseidon2_test.go @@ -61,39 +61,42 @@ func testPoseidon2Hash(s *suite.Suite) { } } -// TODO uncomment once Poseidon2 sponge function is ready -// func testPoseidon2HashSponge(s *suite.Suite) { -// for _, t := range []int{2, 3, 4, 8, 12, 16, 20, 24} { -// inputs := babybear.GenerateScalars(t*8 - 2) -// var noDomainTag *babybear.ScalarField -// -// // Set device to CPU -// test_helpers.ActivateReferenceDevice() -// outputsRef := make([]babybear.ScalarField, 1) -// poseidon2HasherRef, _ := poseidon2.NewHasher(uint64(t), noDomainTag) -// err := poseidon2HasherRef.Hash( -// core.HostSliceFromElements(inputs), -// core.HostSliceFromElements(outputsRef), -// core.GetDefaultHashConfig(), -// ) -// -// poseidonHasherRef.Delete() -// -// // Set device to main -// test_helpers.ActivateMainDevice() -// outputsMain := make([]babybear.ScalarField, 1) -// poseidonHasherMain, _ := poseidon.NewHasher(uint64(t), noDomainTag) -// poseidonHasherMain.Hash( -// core.HostSliceFromElements(inputs), -// core.HostSliceFromElements(outputsMain), -// core.GetDefaultHashConfig(), -// ) -// -// poseidon2HasherMain.Delete() -// -// s.Equal(runtime.InvalidArgument, err) -// } -// } +func testPoseidon2HashSponge(s *suite.Suite) { + for _, t := range []int{2, 3, 4, 8, 12, 16, 20, 24} { + // TODO Danny add 8, 12, 16, 20, 24 for large fields once all is supported + var largeField babybear.ScalarField + if largeField.Size() > 4 && t > 4 { + continue // TODO Danny remove this + } + inputs := babybear.GenerateScalars(t*8 - 2) + + // Set device to CPU + test_helpers.ActivateReferenceDevice() + outputsRef := make([]babybear.ScalarField, 1) + poseidon2HasherRef, _ := poseidon2.NewHasher(uint64(t), nil /*domain tag*/) + poseidon2HasherRef.Hash( + core.HostSliceFromElements(inputs), + core.HostSliceFromElements(outputsRef), + core.GetDefaultHashConfig(), + ) + + poseidon2HasherRef.Delete() + + // Set device to main + test_helpers.ActivateMainDevice() + outputsMain := make([]babybear.ScalarField, 1) + poseidon2HasherMain, _ := poseidon2.NewHasher(uint64(t), nil /*domain tag*/) + poseidon2HasherMain.Hash( + core.HostSliceFromElements(inputs), + core.HostSliceFromElements(outputsMain), + core.GetDefaultHashConfig(), + ) + + poseidon2HasherMain.Delete() + + s.Equal(outputsRef, outputsMain, "Poseidon2 hash outputs did not match") + } +} func testPoseidon2HashTree(s *suite.Suite) { t := 4 @@ -132,7 +135,7 @@ type Poseidon2TestSuite struct { func (s *Poseidon2TestSuite) TestPoseidon2() { s.Run("TestPoseidon2Hash", testWrapper(&s.Suite, testPoseidon2Hash)) - // s.Run("TestPoseidonHash2Sponge", testWrapper(&s.Suite, testPoseidonHash2Sponge)) + s.Run("TestPoseidon2HashSponge", testWrapper(&s.Suite, testPoseidon2HashSponge)) s.Run("TestPoseidon2HashTree", testWrapper(&s.Suite, testPoseidon2HashTree)) } diff --git a/wrappers/golang/fields/koalabear/tests/poseidon2_test.go b/wrappers/golang/fields/koalabear/tests/poseidon2_test.go index b9fe7ec26..edc2ff3a3 100644 --- a/wrappers/golang/fields/koalabear/tests/poseidon2_test.go +++ b/wrappers/golang/fields/koalabear/tests/poseidon2_test.go @@ -61,39 +61,42 @@ func testPoseidon2Hash(s *suite.Suite) { } } -// TODO uncomment once Poseidon2 sponge function is ready -// func testPoseidon2HashSponge(s *suite.Suite) { -// for _, t := range []int{2, 3, 4, 8, 12, 16, 20, 24} { -// inputs := koalabear.GenerateScalars(t*8 - 2) -// var noDomainTag *koalabear.ScalarField -// -// // Set device to CPU -// test_helpers.ActivateReferenceDevice() -// outputsRef := make([]koalabear.ScalarField, 1) -// poseidon2HasherRef, _ := poseidon2.NewHasher(uint64(t), noDomainTag) -// err := poseidon2HasherRef.Hash( -// core.HostSliceFromElements(inputs), -// core.HostSliceFromElements(outputsRef), -// core.GetDefaultHashConfig(), -// ) -// -// poseidonHasherRef.Delete() -// -// // Set device to main -// test_helpers.ActivateMainDevice() -// outputsMain := make([]koalabear.ScalarField, 1) -// poseidonHasherMain, _ := poseidon.NewHasher(uint64(t), noDomainTag) -// poseidonHasherMain.Hash( -// core.HostSliceFromElements(inputs), -// core.HostSliceFromElements(outputsMain), -// core.GetDefaultHashConfig(), -// ) -// -// poseidon2HasherMain.Delete() -// -// s.Equal(runtime.InvalidArgument, err) -// } -// } +func testPoseidon2HashSponge(s *suite.Suite) { + for _, t := range []int{2, 3, 4, 8, 12, 16, 20, 24} { + // TODO Danny add 8, 12, 16, 20, 24 for large fields once all is supported + var largeField koalabear.ScalarField + if largeField.Size() > 4 && t > 4 { + continue // TODO Danny remove this + } + inputs := koalabear.GenerateScalars(t*8 - 2) + + // Set device to CPU + test_helpers.ActivateReferenceDevice() + outputsRef := make([]koalabear.ScalarField, 1) + poseidon2HasherRef, _ := poseidon2.NewHasher(uint64(t), nil /*domain tag*/) + poseidon2HasherRef.Hash( + core.HostSliceFromElements(inputs), + core.HostSliceFromElements(outputsRef), + core.GetDefaultHashConfig(), + ) + + poseidon2HasherRef.Delete() + + // Set device to main + test_helpers.ActivateMainDevice() + outputsMain := make([]koalabear.ScalarField, 1) + poseidon2HasherMain, _ := poseidon2.NewHasher(uint64(t), nil /*domain tag*/) + poseidon2HasherMain.Hash( + core.HostSliceFromElements(inputs), + core.HostSliceFromElements(outputsMain), + core.GetDefaultHashConfig(), + ) + + poseidon2HasherMain.Delete() + + s.Equal(outputsRef, outputsMain, "Poseidon2 hash outputs did not match") + } +} func testPoseidon2HashTree(s *suite.Suite) { t := 4 @@ -132,7 +135,7 @@ type Poseidon2TestSuite struct { func (s *Poseidon2TestSuite) TestPoseidon2() { s.Run("TestPoseidon2Hash", testWrapper(&s.Suite, testPoseidon2Hash)) - // s.Run("TestPoseidonHash2Sponge", testWrapper(&s.Suite, testPoseidonHash2Sponge)) + s.Run("TestPoseidon2HashSponge", testWrapper(&s.Suite, testPoseidon2HashSponge)) s.Run("TestPoseidon2HashTree", testWrapper(&s.Suite, testPoseidon2HashTree)) } diff --git a/wrappers/golang/internal/generator/poseidon2/templates/poseidon2_test.go.tmpl b/wrappers/golang/internal/generator/poseidon2/templates/poseidon2_test.go.tmpl index 1ed07d5f8..6cb4d7275 100644 --- a/wrappers/golang/internal/generator/poseidon2/templates/poseidon2_test.go.tmpl +++ b/wrappers/golang/internal/generator/poseidon2/templates/poseidon2_test.go.tmpl @@ -61,39 +61,43 @@ func testPoseidon2Hash(s *suite.Suite) { } } -// TODO uncomment once Poseidon2 sponge function is ready -// func testPoseidon2HashSponge(s *suite.Suite) { -// for _, t := range []int{2, 3, 4, 8, 12, 16, 20, 24} { -// inputs := {{.Field}}.GenerateScalars(t*8 - 2) -// var noDomainTag *{{.Field}}.ScalarField -// -// // Set device to CPU -// test_helpers.ActivateReferenceDevice() -// outputsRef := make([]{{.Field}}.ScalarField, 1) -// poseidon2HasherRef, _ := poseidon2.NewHasher(uint64(t), noDomainTag) -// err := poseidon2HasherRef.Hash( -// core.HostSliceFromElements(inputs), -// core.HostSliceFromElements(outputsRef), -// core.GetDefaultHashConfig(), -// ) -// -// poseidonHasherRef.Delete() -// -// // Set device to main -// test_helpers.ActivateMainDevice() -// outputsMain := make([]{{.Field}}.ScalarField, 1) -// poseidonHasherMain, _ := poseidon.NewHasher(uint64(t), noDomainTag) -// poseidonHasherMain.Hash( -// core.HostSliceFromElements(inputs), -// core.HostSliceFromElements(outputsMain), -// core.GetDefaultHashConfig(), -// ) -// -// poseidon2HasherMain.Delete() -// -// s.Equal(runtime.InvalidArgument, err) -// } -// } + func testPoseidon2HashSponge(s *suite.Suite) { + for _, t := range []int{2, 3, 4, 8, 12, 16, 20, 24} { + // TODO Danny add 8, 12, 16, 20, 24 for large fields once all is supported + var largeField {{.Field}}.ScalarField + if largeField.Size() > 4 && t > 4 { + continue // TODO Danny remove this + } + inputs := {{.Field}}.GenerateScalars(t*8 - 2) + + + // Set device to CPU + test_helpers.ActivateReferenceDevice() + outputsRef := make([]{{.Field}}.ScalarField, 1) + poseidon2HasherRef, _ := poseidon2.NewHasher(uint64(t), nil /*domain tag*/) + poseidon2HasherRef.Hash( + core.HostSliceFromElements(inputs), + core.HostSliceFromElements(outputsRef), + core.GetDefaultHashConfig(), + ) + + poseidon2HasherRef.Delete() + + // Set device to main + test_helpers.ActivateMainDevice() + outputsMain := make([]{{.Field}}.ScalarField, 1) + poseidon2HasherMain, _ := poseidon2.NewHasher(uint64(t), nil /*domain tag*/) + poseidon2HasherMain.Hash( + core.HostSliceFromElements(inputs), + core.HostSliceFromElements(outputsMain), + core.GetDefaultHashConfig(), + ) + + poseidon2HasherMain.Delete() + + s.Equal(outputsRef, outputsMain, "Poseidon2 hash outputs did not match") + } + } func testPoseidon2HashTree(s *suite.Suite) { t := 4 @@ -132,7 +136,7 @@ type Poseidon2TestSuite struct { func (s *Poseidon2TestSuite) TestPoseidon2() { s.Run("TestPoseidon2Hash", testWrapper(&s.Suite, testPoseidon2Hash)) - // s.Run("TestPoseidonHash2Sponge", testWrapper(&s.Suite, testPoseidonHash2Sponge)) + s.Run("TestPoseidon2HashSponge", testWrapper(&s.Suite, testPoseidon2HashSponge)) s.Run("TestPoseidon2HashTree", testWrapper(&s.Suite, testPoseidon2HashTree)) } diff --git a/wrappers/rust/icicle-core/src/poseidon2/mod.rs b/wrappers/rust/icicle-core/src/poseidon2/mod.rs index c8401e7da..72d612ae0 100644 --- a/wrappers/rust/icicle-core/src/poseidon2/mod.rs +++ b/wrappers/rust/icicle-core/src/poseidon2/mod.rs @@ -96,12 +96,11 @@ macro_rules! impl_poseidon2_tests { check_poseidon2_hash::<$field>(); } - // TODO uncomment when feature is ready - // #[test] - // fn test_poseidon2_hash_sponge() { - // initialize(); - // check_poseidon2_hash_sponge::<$field>(); - // } + #[test] + fn test_poseidon2_hash_sponge() { + initialize(); + check_poseidon2_hash_sponge::<$field>(); + } #[test] fn test_poseidon2_hash_multi_device() { diff --git a/wrappers/rust/icicle-core/src/poseidon2/tests.rs b/wrappers/rust/icicle-core/src/poseidon2/tests.rs index b17349a06..333eb7946 100644 --- a/wrappers/rust/icicle-core/src/poseidon2/tests.rs +++ b/wrappers/rust/icicle-core/src/poseidon2/tests.rs @@ -15,9 +15,9 @@ where let domain_tag = F::Config::generate_random(1)[0]; for t in [2, 3, 4, 8, 12, 16, 20, 24] { let large_field = mem::size_of::() > 4; - let skip_case = large_field && t > 4; // TODO Danny add 8, 12, 16, 20, 24 for large fields once all is supported + let skip_case = large_field && t > 4; // In Poseidon2 there is no support for large fields when t > 4. if skip_case { - continue; // TODO Danny remove this + continue; }; for domain_tag in [None, Some(&domain_tag)] { let inputs: Vec = if domain_tag != None { @@ -50,44 +50,49 @@ where ) .unwrap(); - assert_eq!(outputs_main, outputs_ref); + assert_eq!(outputs_main, outputs_ref, "Domain tag {:?}, t {:?}", domain_tag, t); } } } // TODO uncomment once Poseidon2 sponge function is ready -// pub fn check_poseidon2_hash_sponge() -// where -// ::Config: Poseidon2Hasher + GenerateRandom, -// { -// for t in [2, 3, 4, 8, 12, 16, 20, 24] { -// let inputs: Vec = F::Config::generate_random(t * 8 - 2); -// let mut outputs_main = vec![F::zero(); 1]; -// let mut outputs_ref = vec![F::zero(); 1]; - -// test_utilities::test_set_main_device(); -// let poseidon_hasher_main = Poseidon2::new::(t as u32, None /*domain_tag*/).unwrap(); - -// poseidon_hasher_main -// .hash( -// HostSlice::from_slice(&inputs), -// &HashConfig::default(), -// HostSlice::from_mut_slice(&mut outputs_main), -// ) -// .unwrap(); - -// // Sponge poseidon is planned for v3.2. Not supported in v3.1 -// test_utilities::test_set_ref_device(); -// let poseidon_hasher_ref = Poseidon2::new::(t as u32, None /*domain_tag*/).unwrap(); - -// let err = poseidon_hasher_ref.hash( -// HostSlice::from_slice(&inputs), -// &HashConfig::default(), -// HostSlice::from_mut_slice(&mut outputs_ref), -// ); -// assert_eq!(err, Err(eIcicleError::InvalidArgument)); -// } -// } +pub fn check_poseidon2_hash_sponge() +where + ::Config: Poseidon2Hasher + GenerateRandom, +{ + for t in [2, 3, 4, 8, 12, 16, 20, 24] { + let large_field = mem::size_of::() > 4; + let skip_case = large_field && t > 4; // In Poseidon2 there is no support for large fields when t > 4. + if skip_case { + continue; + }; + let inputs: Vec = F::Config::generate_random(t * 8 - 2); + let mut outputs_main = vec![F::zero(); 1]; + let mut outputs_ref = vec![F::zero(); 1]; + + test_utilities::test_set_main_device(); + let poseidon_hasher_main = Poseidon2::new::(t as u32, None /*domain_tag*/).unwrap(); + + poseidon_hasher_main + .hash( + HostSlice::from_slice(&inputs), + &HashConfig::default(), + HostSlice::from_mut_slice(&mut outputs_main), + ) + .unwrap(); + + // Sponge poseidon is planned for v3.2. Not supported in v3.1 + test_utilities::test_set_ref_device(); + let poseidon_hasher_ref = Poseidon2::new::(t as u32, None /*domain_tag*/).unwrap(); + + let err = poseidon_hasher_ref.hash( + HostSlice::from_slice(&inputs), + &HashConfig::default(), + HostSlice::from_mut_slice(&mut outputs_ref), + ); + // assert_eq!(err, Err(eIcicleError::InvalidArgument)); + } +} pub fn check_poseidon2_hash_multi_device() where