diff --git a/libs/zk/include/nil/crypto3/zk/commitments/detail/polynomial/basic_fri.hpp b/libs/zk/include/nil/crypto3/zk/commitments/detail/polynomial/basic_fri.hpp index 5db51d804..9fb233588 100644 --- a/libs/zk/include/nil/crypto3/zk/commitments/detail/polynomial/basic_fri.hpp +++ b/libs/zk/include/nil/crypto3/zk/commitments/detail/polynomial/basic_fri.hpp @@ -5,6 +5,7 @@ // Copyright (c) 2021-2022 Aleksei Moskvin // Copyright (c) 2022 Ilias Khairullin // Copyright (c) 2022-2023 Elena Tatuzova +// Copyright (c) 2024 Vasiliy Olekhov // // MIT License // @@ -583,7 +584,7 @@ namespace nil { template static inline std::pair>, std::vector>> - calculate_s(const typename FRI::field_type::value_type &x, const std::size_t x_index, + calculate_s(const std::size_t x_index, const std::size_t fri_step, std::shared_ptr> D) { const std::size_t domain_size = D->size(); @@ -594,7 +595,6 @@ namespace nil { s_indices[0][1] = get_paired_index(s_indices[0][0], domain_size); s[0][0] = D->get_domain_element(s_indices[0][0]); s[0][1] = D->get_domain_element(s_indices[0][1]); - BOOST_ASSERT(s[0][0] == x); // [0, N/4, N/8, N/8 + N/4, N/16, N/16 + N/4, N/16 + N/8, N/16 + N/8 + N/4 ...] std::size_t base_index = domain_size / (FRI::m * FRI::m); std::size_t prev_half_size = 1; @@ -664,53 +664,22 @@ namespace nil { return correct_order_idx; } - template, - FRI>::value, - bool>::type = true> - static typename FRI::proof_type proof_eval( - const std::map> &g, - const PolynomialType combined_Q, + template + static void commit_phase( + const PolynomialType& combined_Q, const std::map &precommitments, const typename FRI::precommitment_type &combined_Q_precommitment, const typename FRI::params_type &fri_params, - typename FRI::transcript_type &transcript - ) { - typename FRI::proof_type proof; - - BOOST_ASSERT(check_step_list(fri_params)); - // TODO: add necessary checks - //BOOST_ASSERT(check_initial_precommitment(precommitments, fri_params)); - - // This resizes actually happens when called at the end of prover: - // _proof.eval_proof.eval_proof = _commitment_scheme.proof_eval(transcript); - // We DO NOT resize it here, it takes waaay too much RAM, resize it when needed. - - //if constexpr (std::is_same, PolynomialType>::value) { - // for( auto const &it:g ){ - // auto k = it.first; - // for (int i = 0; i < g[k].size(); ++i ){ - // // If LPC works properly this if is never executed. - // if (g[k][i].size() != fri_params.D[0]->size()) { - // g[k][i].resize(fri_params.D[0]->size()); - // } - // } - // } - //} - + typename FRI::transcript_type &transcript, + std::vector &fs, + std::vector &fri_trees, + std::vector &fri_roots, + math::polynomial &final_polynomial) + { // Commit phase + PROFILE_SCOPE("Basic FRI commit phase"); auto f = combined_Q; auto precommitment = combined_Q_precommitment; - - std::vector fri_trees; - std::vector fri_roots; - std::vector alphas; - std::vector fs; std::size_t t = 0; for (std::size_t i = 0; i < fri_params.step_list.size(); i++) { @@ -718,41 +687,42 @@ namespace nil { fri_trees.push_back(precommitment); fri_roots.push_back(commit(precommitment)); transcript(commit(precommitment)); - for (std::size_t step_i = 0; step_i < fri_params.step_list[i]; step_i++, t++) { - alphas.push_back(transcript.template challenge()); - // Calculate next f. + for (std::size_t step_i = 0; step_i < fri_params.step_list[i]; ++step_i, ++t) { + typename FRI::field_type::value_type alpha = transcript.template challenge(); + // Calculate next f if constexpr (std::is_same, PolynomialType>::value) { - f = commitments::detail::fold_polynomial(f, alphas[t], + f = commitments::detail::fold_polynomial(f, alpha, fri_params.D[t]); } else { - f = commitments::detail::fold_polynomial(f, alphas[t]); + f = commitments::detail::fold_polynomial(f, alpha); } } if (i != fri_params.step_list.size() - 1) precommitment = precommit(f, fri_params.D[t], fri_params.step_list[i + 1]); } fs.push_back(f); - math::polynomial final_polynomial; if constexpr (std::is_same, PolynomialType>::value) { final_polynomial = math::polynomial(f.coefficients()); } else { final_polynomial = f; } + } - // Grinding - if( fri_params.use_grinding ){ - proof.proof_of_work = FRI::grinding_type::generate(transcript, fri_params.grinding_parameter); - } - - // Query phase - std::vector query_proofs(fri_params.lambda); + /** @brief Convert a set of polynomials from DFS form into coefficients form */ + template + static std::map< + std::size_t, + std::vector> + > convert_polynomials_to_coefficients( + typename FRI::params_type const& fri_params, + std::map> const& g) + { + std::map< + std::size_t, + std::vector> + > g_coeffs; - // If we have DFS polynomials, and we are going to resize them, better convert them to coefficients form, - // and compute their values in those 2 * FRI::lambda points each, which is normally 2 * 20. - // In case lambda becomes much larger than log(2, average polynomial size), then this will not be optimal. - // For lambda = 20 and 2^20 rows in assignment table, it's faster and uses less RAM. - std::map>> g_coeffs; if constexpr (std::is_same< math::polynomial_dfs, PolynomialType>::value @@ -774,73 +744,58 @@ namespace nil { } } } + return std::move(g_coeffs); + } - for (std::size_t query_id = 0; query_id < fri_params.lambda; query_id++) { - std::size_t domain_size = fri_params.D[0]->size(); - typename FRI::field_type::value_type x = transcript.template challenge(); - x = x.pow((FRI::field_type::modulus - 1)/domain_size); - std::uint64_t x_index = 0; - for( x_index = 0; x_index < domain_size; x_index++ ){ - if( fri_params.D[0]->get_domain_element(x_index) == x ){ - break; - } - } - t = 0; - - std::vector> s; - std::vector> s_indices; - std::tie(s, s_indices) = calculate_s(x, x_index, fri_params.step_list[0], fri_params.D[0]); - //Initial proof - std::map initial_proof; - for (const auto &it: g) { - auto k = it.first; - initial_proof[k] = {}; - initial_proof[k].values.resize(it.second.size()); - std::size_t coset_size = 1 << fri_params.step_list[0]; - BOOST_ASSERT(coset_size / FRI::m == s.size()); - BOOST_ASSERT(coset_size / FRI::m == s_indices.size()); + template + static std::map + build_initial_proof( + std::map const& precommitments, + typename FRI::params_type const& fri_params, + std::map> const& g, + std::map< + std::size_t, + std::vector> + > const& g_coeffs, + std::uint64_t x_index) + { + std::vector> s; + std::vector> s_indices; + std::tie(s, s_indices) = calculate_s(x_index, fri_params.step_list[0], fri_params.D[0]); + + std::map initial_proof; + + for (const auto &it: g) { + auto k = it.first; + initial_proof[k] = {}; + initial_proof[k].values.resize(it.second.size()); + std::size_t coset_size = 1 << fri_params.step_list[0]; + BOOST_ASSERT(coset_size / FRI::m == s.size()); + BOOST_ASSERT(coset_size / FRI::m == s_indices.size()); - //Fill values - t = 0; - const auto& g_k = it.second; // g[k] + // Fill values + const auto& g_k = it.second; // g[k] - for (std::size_t polynomial_index = 0; polynomial_index < g_k.size(); ++polynomial_index) { - initial_proof[k].values[polynomial_index].resize(coset_size / FRI::m); - if constexpr (std::is_same< - math::polynomial_dfs, - PolynomialType>::value + for (std::size_t polynomial_index = 0; polynomial_index < g_k.size(); ++polynomial_index) { + initial_proof[k].values[polynomial_index].resize(coset_size / FRI::m); + if constexpr (std::is_same< + math::polynomial_dfs, + PolynomialType>::value ) { - if (g_k[polynomial_index].size() == fri_params.D[0]->size()) { - for (std::size_t j = 0; j < coset_size / FRI::m; j++) { - std::size_t ind0 = std::min(s_indices[j][0], s_indices[j][1]); - std::size_t ind1 = std::max(s_indices[j][0], s_indices[j][1]); - initial_proof[k].values[polynomial_index][j][0] = g_k[polynomial_index][ind0]; - initial_proof[k].values[polynomial_index][j][1] = g_k[polynomial_index][ind1]; - } - } else { - // Convert to coefficients form and evaluate. coset_size / FRI::m is usually just 1, - // It makes no sense to resize in dfs form to then use just 2 values in 2 points. - for (std::size_t j = 0; j < coset_size / FRI::m; j++) { - typename FRI::field_type::value_type s0; - typename FRI::field_type::value_type s1; - if( s_indices[j][0] < s_indices[j][1]){ - s0 = s[j][0]; - s1 = s[j][1]; - } else { - s0 = s[j][1]; - s1 = s[j][0]; - } - initial_proof[k].values[polynomial_index][j][0] = g_coeffs[k][polynomial_index].evaluate(s0); - initial_proof[k].values[polynomial_index][j][1] = g_coeffs[k][polynomial_index].evaluate(s1); - } + if (g_k[polynomial_index].size() == fri_params.D[0]->size()) { + for (std::size_t j = 0; j < coset_size / FRI::m; j++) { + std::size_t ind0 = std::min(s_indices[j][0], s_indices[j][1]); + std::size_t ind1 = std::max(s_indices[j][0], s_indices[j][1]); + initial_proof[k].values[polynomial_index][j][0] = g_k[polynomial_index][ind0]; + initial_proof[k].values[polynomial_index][j][1] = g_k[polynomial_index][ind1]; } } else { - // Same for poly in coefficients form. + // Convert to coefficients form and evaluate. coset_size / FRI::m is usually just 1, + // It makes no sense to resize in dfs form to then use just 2 values in 2 points. for (std::size_t j = 0; j < coset_size / FRI::m; j++) { typename FRI::field_type::value_type s0; typename FRI::field_type::value_type s1; - if( s_indices[j][0] < s_indices[j][1]){ s0 = s[j][0]; s1 = s[j][1]; @@ -848,81 +803,213 @@ namespace nil { s0 = s[j][1]; s1 = s[j][0]; } - initial_proof[k].values[polynomial_index][j][0] = g_k[polynomial_index].evaluate(s0); - initial_proof[k].values[polynomial_index][j][1] = g_k[polynomial_index].evaluate(s1); + initial_proof[k].values[polynomial_index][j][0] = g_coeffs.at(k)[polynomial_index].evaluate(s0); + initial_proof[k].values[polynomial_index][j][1] = g_coeffs.at(k)[polynomial_index].evaluate(s1); + } + } + } else { + // Same for poly in coefficients form. + for (std::size_t j = 0; j < coset_size / FRI::m; j++) { + typename FRI::field_type::value_type s0; + typename FRI::field_type::value_type s1; + + if( s_indices[j][0] < s_indices[j][1]){ + s0 = s[j][0]; + s1 = s[j][1]; + } else { + s0 = s[j][1]; + s1 = s[j][0]; } + initial_proof[k].values[polynomial_index][j][0] = g_k[polynomial_index].evaluate(s0); + initial_proof[k].values[polynomial_index][j][1] = g_k[polynomial_index].evaluate(s1); } } + } - // Fill merkle proofs - initial_proof[k].p = make_proof_specialized( + // Fill merkle proofs + initial_proof[k].p = make_proof_specialized( get_folded_index(x_index, fri_params.D[0]->size(), fri_params.step_list[0]), - fri_params.D[0]->size(), precommitments.at(k) - ); - } + fri_params.D[0]->size(), precommitments.at(k) ); + } - // Fill round proofs - std::vector round_proofs; - t = 0; - round_proofs.resize(fri_params.step_list.size()); - for (std::size_t i = 0; i < fri_params.step_list.size(); i++) { - domain_size = fri_params.D[t]->size(); - x_index %= domain_size; - x = fri_params.D[t]->get_domain_element(x_index); - round_proofs[i].p = make_proof_specialized( - get_folded_index(x_index, domain_size, fri_params.step_list[i]), - domain_size, fri_trees[i] - ); + return std::move(initial_proof); + } - t += fri_params.step_list[i]; - if (i < fri_params.step_list.size() - 1) { - x_index %= fri_params.D[t]->size(); - x = fri_params.D[t]->get_domain_element(x_index); - std::tie(s, s_indices) = calculate_s( - x, x_index, fri_params.step_list[i + 1], fri_params.D[t] - ); + template + static std::vector + build_round_proofs( + typename FRI::params_type const& fri_params, + std::map> const& g, + std::map< + std::size_t, + std::vector> + > const& g_coeffs, + std::vector const& fri_trees, + std::vector const& fs, + math::polynomial const& final_polynomial, + std::uint64_t x_index) + { + std::size_t domain_size = fri_params.D[0]->size(); + std::size_t t = 0; + std::vector round_proofs(fri_params.step_list.size()); - std::size_t coset_size = 1 << fri_params.step_list[i + 1]; - BOOST_ASSERT(coset_size / FRI::m == s.size()); - BOOST_ASSERT(coset_size / FRI::m == s_indices.size()); + for (std::size_t i = 0; i < fri_params.step_list.size(); i++) { - round_proofs[i].y.resize(coset_size / FRI::m); - for (std::size_t j = 0; j < coset_size / FRI::m; j++) { - if constexpr (std::is_same, - PolynomialType>::value) { - std::size_t ind0 = std::min(s_indices[j][0], s_indices[j][1]); - std::size_t ind1 = std::max(s_indices[j][0], s_indices[j][1]); - round_proofs[i].y[j][0] = fs[i + 1][ind0]; - round_proofs[i].y[j][1] = fs[i + 1][ind1]; - } else { - typename FRI::field_type::value_type s0 = (s_indices[j][0] < s_indices[j][1] ? s[j][0] : s[j][1]); - typename FRI::field_type::value_type s1 = (s_indices[j][0] > s_indices[j][1] ? s[j][0] : s[j][1]); - round_proofs[i].y[j][0] = fs[i + 1].evaluate(s0); - round_proofs[i].y[j][1] = fs[i + 1].evaluate(s1); - } + domain_size = fri_params.D[t]->size(); + x_index %= domain_size; + + round_proofs[i].p = make_proof_specialized( + get_folded_index(x_index, domain_size, fri_params.step_list[i]), + domain_size, fri_trees[i]); + + t += fri_params.step_list[i]; + if (i < fri_params.step_list.size() - 1) { + x_index %= fri_params.D[t]->size(); + + std::vector> s; + std::vector> s_indices; + std::tie(s, s_indices) = calculate_s(x_index, fri_params.step_list[i + 1], fri_params.D[t]); + + std::size_t coset_size = 1 << fri_params.step_list[i + 1]; + BOOST_ASSERT(coset_size / FRI::m == s.size()); + BOOST_ASSERT(coset_size / FRI::m == s_indices.size()); + + round_proofs[i].y.resize(coset_size / FRI::m); + for (std::size_t j = 0; j < coset_size / FRI::m; j++) { + if constexpr (std::is_same, + PolynomialType>::value) { + std::size_t ind0 = std::min(s_indices[j][0], s_indices[j][1]); + std::size_t ind1 = std::max(s_indices[j][0], s_indices[j][1]); + round_proofs[i].y[j][0] = fs[i + 1][ind0]; + round_proofs[i].y[j][1] = fs[i + 1][ind1]; + } else { + typename FRI::field_type::value_type s0 = (s_indices[j][0] < s_indices[j][1] ? s[j][0] : s[j][1]); + typename FRI::field_type::value_type s1 = (s_indices[j][0] > s_indices[j][1] ? s[j][0] : s[j][1]); + round_proofs[i].y[j][0] = fs[i + 1].evaluate(s0); + round_proofs[i].y[j][1] = fs[i + 1].evaluate(s1); } - } else { - x_index %= fri_params.D[t - 1]->size(); + } + } else { + x_index %= fri_params.D[t - 1]->size(); + + typename FRI::field_type::value_type x = fri_params.D[t - 1]->get_domain_element(x_index); - x = x * x; + x *= x; + + // Last step + // Assume that FRI rounds continues with step == 1 + // x_index % (domain_size / 2) -- index in the next round + // fri_params.D[t-1]->size()/4 -- half of the next domain size + // Then next round values will be written in the straight order if next round index < next domain size - 1 + // Otherwise, they will be written in the reverse order. + + std::size_t ind = (x_index %(fri_params.D[t-1]->size()/2) < fri_params.D[t-1]->size()/4)? 0: 1; + round_proofs[i].y.resize(1); + round_proofs[i].y[0][ind] = final_polynomial.evaluate(x); + round_proofs[i].y[0][1-ind] = final_polynomial.evaluate(-x); + } + } + return std::move(round_proofs); + } + + template, + FRI>::value, + bool>::type = true> + static typename FRI::proof_type proof_eval( + const std::map> &g, + const PolynomialType& combined_Q, + const std::map &precommitments, + const typename FRI::precommitment_type &combined_Q_precommitment, + const typename FRI::params_type &fri_params, + typename FRI::transcript_type &transcript + ) { + typename FRI::proof_type proof; + + BOOST_ASSERT(check_step_list(fri_params)); + // TODO: add necessary checks + //BOOST_ASSERT(check_initial_precommitment(precommitments, fri_params)); + + // This resizes actually happens when called at the end of prover: + // _proof.eval_proof.eval_proof = _commitment_scheme.proof_eval(transcript); + // We DO NOT resize it here, it takes waaay too much RAM, resize it when needed. - // Last step - // Assume that FRI rounds continues with step == 1 - // x_index % (domain_size / 2) -- index in the next round - // fri_params.D[t-1]->size()/4 -- half of the next domain size - // Then next round values will be written in the straight order if next round index < next domain size - 1 - // Otherwise, they will be written in the reverse order. - - std::size_t ind = (x_index %(fri_params.D[t-1]->size()/2) < fri_params.D[t-1]->size()/4)? 0: 1; - round_proofs[i].y.resize(1); - round_proofs[i].y[0][ind] = final_polynomial.evaluate(x); - round_proofs[i].y[0][1-ind] = final_polynomial.evaluate(-x); + //if constexpr (std::is_same, PolynomialType>::value) { + // for( auto const &it:g ){ + // auto k = it.first; + // for (int i = 0; i < g[k].size(); ++i ){ + // // If LPC works properly this if is never executed. + // if (g[k][i].size() != fri_params.D[0]->size()) { + // g[k][i].resize(fri_params.D[0]->size()); + // } + // } + // } + //} + + // Commit phase + + std::vector fri_trees; + std::vector fri_roots; + std::vector fs; + math::polynomial final_polynomial; + + commit_phase( + combined_Q, precommitments, + combined_Q_precommitment, + fri_params, transcript, fs, + fri_trees, fri_roots, final_polynomial); + + // Grinding + if ( fri_params.use_grinding ) { + PROFILE_SCOPE("Basic FRI grinding phase"); + proof.proof_of_work = FRI::grinding_type::generate(transcript, fri_params.grinding_parameter); + } + + // Query phase + std::vector query_proofs(fri_params.lambda); + + // If we have DFS polynomials, and we are going to resize them, better convert them to coefficients form, + // and compute their values in those 2 * FRI::lambda points each, which is normally 2 * 20. + // In case lambda becomes much larger than log(2, average polynomial size), then this will not be optimal. + // For lambda = 20 and 2^20 rows in assignment table, it's faster and uses less RAM. + { + PROFILE_SCOPE("Basic FRI query phase"); + std::map>> g_coeffs = + convert_polynomials_to_coefficients(fri_params, g); + + for (std::size_t query_id = 0; query_id < fri_params.lambda; query_id++) { + std::size_t domain_size = fri_params.D[0]->size(); + typename FRI::field_type::value_type x = transcript.template challenge(); + x = x.pow((FRI::field_type::modulus - 1)/domain_size); + + std::uint64_t x_index = 0; + + while (fri_params.D[0]->get_domain_element(x_index) != x) { + ++x_index; + if (x_index >= domain_size) { + // unreachable } } + + //Initial proof + std::map + initial_proof = build_initial_proof(precommitments, fri_params, g, g_coeffs, x_index); + + // Fill round proofs + std::vector + round_proofs = build_round_proofs(fri_params, g, g_coeffs, fri_trees, fs, final_polynomial, x_index); + typename FRI::query_proof_type query_proof = {std::move(initial_proof), std::move(round_proofs)}; query_proofs[query_id] = std::move(query_proof); } + } // profile + proof.fri_roots = std::move(fri_roots); proof.final_polynomial = std::move(final_polynomial); proof.query_proofs = std::move(query_proofs); @@ -981,7 +1068,7 @@ namespace nil { std::vector> s; std::vector> s_indices; - std::tie(s, s_indices) = calculate_s(x, x_index, fri_params.step_list[0], fri_params.D[0]); + std::tie(s, s_indices) = calculate_s(x_index, fri_params.step_list[0], fri_params.D[0]); auto correct_order_idx = get_correct_order(x_index, domain_size, fri_params.step_list[0], s_indices); @@ -1044,7 +1131,7 @@ namespace nil { coset_size = 1 << fri_params.step_list[i]; if (query_proof.round_proofs[i].p.root() != proof.fri_roots[i]) return false; - std::tie(s, s_indices) = calculate_s(x, x_index, fri_params.step_list[i], + std::tie(s, s_indices) = calculate_s(x_index, fri_params.step_list[i], fri_params.D[t]); detail::fri_field_element_consumer leaf_data(coset_size); auto correct_order_idx = @@ -1065,12 +1152,15 @@ namespace nil { domain_size = fri_params.D[t]->size(); x_index %= domain_size; x = fri_params.D[t]->get_domain_element(x_index); + auto [s_next, s_indices_next] = calculate_s( - x *x , x_index% fri_params.D[t+1]->size(), + x_index % fri_params.D[t+1]->size(), fri_params.step_list[i], fri_params.D[t+1] ); - std::tie(s, s_indices) = calculate_s(x, x_index, fri_params.step_list[i], - fri_params.D[t]); + + std::tie(s, s_indices) = calculate_s( + x_index, fri_params.step_list[i], fri_params.D[t]); + std::size_t new_domain_size = domain_size; for (std::size_t y_ind = 0; y_ind < y_next.size(); y_ind++) { std::size_t ind0 = s_indices[2 * y_ind][0] < s_indices[2 * y_ind][1] ? 0 : 1; @@ -1111,8 +1201,9 @@ namespace nil { domain_size = fri_params.D[t]->size(); x_index %= domain_size; x = fri_params.D[t]->get_domain_element(x_index); - std::tie(s, s_indices) = calculate_s(x, x_index, fri_params.step_list[i], - fri_params.D[t]); + std::tie(s, s_indices) = calculate_s( + x_index, fri_params.step_list[i], + fri_params.D[t]); std::size_t ind0 = s_indices[0][0] < s_indices[0][1] ? 0 : 1; auto s_ch = s[0][ind0]; diff --git a/libs/zk/include/nil/crypto3/zk/commitments/detail/polynomial/fold_polynomial.hpp b/libs/zk/include/nil/crypto3/zk/commitments/detail/polynomial/fold_polynomial.hpp index 8ab52f7de..8e5a9e331 100644 --- a/libs/zk/include/nil/crypto3/zk/commitments/detail/polynomial/fold_polynomial.hpp +++ b/libs/zk/include/nil/crypto3/zk/commitments/detail/polynomial/fold_polynomial.hpp @@ -78,8 +78,8 @@ namespace nil { math::polynomial_dfs f_folded( domain->size() / 2 - 1, domain->size() / 2, FieldType::value_type::zero()); - typename FieldType::value_type two_inversed = 2u; - two_inversed = two_inversed.inversed(); + constexpr typename FieldType::value_type two_inversed = + typename FieldType::value_type(2u).inversed(); typename FieldType::value_type omega_inversed = domain->get_domain_element(domain->size() - 1); typename FieldType::value_type acc = alpha; diff --git a/libs/zk/test/commitment/fri.cpp b/libs/zk/test/commitment/fri.cpp index 531c4c4d3..cb0bbf5d0 100644 --- a/libs/zk/test/commitment/fri.cpp +++ b/libs/zk/test/commitment/fri.cpp @@ -4,6 +4,7 @@ // Copyright (c) 2022 Ilia Shirobokov // Copyright (c) 2022 Ilias Khairullin // Copyright (c) 2022 Aleksei Moskvin +// Copyright (c) 2024 Vasiliy Olekhov // // MIT License // @@ -134,16 +135,112 @@ BOOST_AUTO_TEST_SUITE(fri_test_suite) std::vector init_blob{0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u}; zk::transcript::fiat_shamir_heuristic_sequential transcript(init_blob); - proof_type proof = zk::algorithms::proof_eval(f, tree, params, transcript); + proof_type proof; + { + PROFILE_SCOPE("FRI proof_eval"); + proof = zk::algorithms::proof_eval(f, tree, params, transcript); + } + + // verify + zk::transcript::fiat_shamir_heuristic_sequential transcript_verifier(init_blob); + + { + PROFILE_SCOPE("FRI verify_eval"); + BOOST_CHECK(zk::algorithms::verify_eval(proof, root, params, transcript_verifier)); + } + + typename FieldType::value_type verifier_next_challenge = transcript_verifier.template challenge(); + typename FieldType::value_type prover_next_challenge = transcript.template challenge(); + BOOST_CHECK(verifier_next_challenge == prover_next_challenge); + } + +template +inline math::polynomial_dfs generate_random_polynomial( + std::size_t degree, + nil::crypto3::random::algebraic_engine &rnd +) { + std::vector coefficients(degree+1); + std::generate(std::begin(coefficients), std::end(coefficients), [&rnd]() { return rnd(); }); + math::polynomial_dfs result; + result.from_coefficients(coefficients); + return result; +} + + + BOOST_AUTO_TEST_CASE(fri_basic_test_dfs) { + + // setup + using curve_type = algebra::curves::pallas; + using FieldType = typename curve_type::base_field_type; + + typedef hashes::sha2<256> merkle_hash_type; + typedef hashes::sha2<256> transcript_hash_type; + + constexpr static const std::size_t d = 16; + + constexpr static const std::size_t r = boost::static_log2::value; + constexpr static const std::size_t m = 2; + constexpr static const std::size_t lambda = 40; + + typedef zk::commitments::fri fri_type; + + static_assert(zk::is_commitment::value); + static_assert(!zk::is_commitment::value); + + typedef typename fri_type::proof_type proof_type; + typedef typename fri_type::params_type params_type; + + + constexpr static const std::size_t d_extended = d; + std::size_t extended_log = boost::static_log2::value; + std::vector>> D = + math::calculate_domain_set(extended_log, r); + + params_type params( + d - 1, // max_degree + D, + generate_random_step_list(r, 3), + 2, //expand_factor + lambda, + true, + 20 + ); + + BOOST_CHECK(D[1]->m == D[0]->m / 2); + BOOST_CHECK(D[1]->get_domain_element(1) == D[0]->get_domain_element(1).squared()); + + auto rnd = nil::crypto3::random::algebraic_engine(0x1337); + + // commit + math::polynomial_dfs + f = generate_random_polynomial(d, rnd); + + typename fri_type::merkle_tree_type tree = zk::algorithms::precommit(f, params.D[0], + params.step_list[0]); + auto root = zk::algorithms::commit(tree); + + // eval + std::vector init_blob{0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u}; + zk::transcript::fiat_shamir_heuristic_sequential transcript(init_blob); + + proof_type proof; + { + PROFILE_SCOPE("FRI proof_eval"); + proof = zk::algorithms::proof_eval(f, tree, params, transcript); + } // verify zk::transcript::fiat_shamir_heuristic_sequential transcript_verifier(init_blob); + { + PROFILE_SCOPE("FRI verify_eval"); BOOST_CHECK(zk::algorithms::verify_eval(proof, root, params, transcript_verifier)); + } typename FieldType::value_type verifier_next_challenge = transcript_verifier.template challenge(); typename FieldType::value_type prover_next_challenge = transcript.template challenge(); BOOST_CHECK(verifier_next_challenge == prover_next_challenge); } + BOOST_AUTO_TEST_SUITE_END()