From 1ca01f06bd73dfc5c66bd93420ea7d78e13a42aa Mon Sep 17 00:00:00 2001 From: Vasiliy Olekhov Date: Fri, 22 Mar 2024 11:06:47 +0200 Subject: [PATCH] Working MNT4 pairing components: Miller loop and Final exponentiation #355 --- cmake/modules | 2 +- .../detail/plonk/mnt4_g2_point_addition.hpp | 385 +++++++++ .../detail/plonk/mnt6_g2_point_addition.hpp | 399 ++++++++++ .../plonk/non_native/detail/abstract_fp2.hpp | 13 +- .../plonk/non_native/detail/abstract_fp3.hpp | 77 ++ .../plonk/non_native/detail/abstract_fp4.hpp | 87 ++ .../plonk/non_native/detail/mnt4_fp4.hpp | 86 ++ .../plonk/non_native/detail/mnt6_fp6.hpp | 100 +++ .../plonk/non_native/mnt4_fp4_fixed_power.hpp | 510 ++++++++++++ .../plonk/non_native/mnt6_fp6_fixed_power.hpp | 495 ++++++++++++ .../weierstrass/plonk/bls12_381_pairing.hpp | 295 +++++++ .../plonk/bls12_exponentiation.hpp | 498 ++++++++++++ .../weierstrass/plonk/bn_exponentiation.hpp | 490 ++++++++++++ .../weierstrass/plonk/detail/fp12_power_t.hpp | 432 ++++++++++ .../detail/fp12_power_tminus1sq_over3.hpp | 527 ++++++++++++ .../pairing/weierstrass/plonk/miller_loop.hpp | 579 ++++++++++++++ .../weierstrass/plonk/mnt4_exponentiation.hpp | 439 ++++++++++ .../weierstrass/plonk/mnt4_miller_loop.hpp | 751 ++++++++++++++++++ test/CMakeLists.txt | 5 + test/algebra/curves/plonk/mnt4_g2.cpp | 304 +++++++ test/algebra/curves/plonk/mnt6_g2.cpp | 360 +++++++++ .../plonk/non_native/fp2_arithmetic.cpp | 149 ++++ .../plonk/non_native/mnt4_fp4_fixed_power.cpp | 160 ++++ .../weierstrass/plonk/mnt4_exponentiation.cpp | 138 ++++ .../weierstrass/plonk/mnt4_pairing.cpp | 147 ++++ test/test_plonk_component.hpp | 19 +- 26 files changed, 7436 insertions(+), 11 deletions(-) create mode 100644 include/nil/blueprint/components/algebra/curves/detail/plonk/mnt4_g2_point_addition.hpp create mode 100644 include/nil/blueprint/components/algebra/curves/detail/plonk/mnt6_g2_point_addition.hpp create mode 100644 include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp3.hpp create mode 100644 include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp4.hpp create mode 100644 include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/mnt4_fp4.hpp create mode 100644 include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/mnt6_fp6.hpp create mode 100644 include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp create mode 100644 include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp create mode 100644 include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/bls12_381_pairing.hpp create mode 100644 include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/bls12_exponentiation.hpp create mode 100644 include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/bn_exponentiation.hpp create mode 100644 include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/detail/fp12_power_t.hpp create mode 100644 include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/detail/fp12_power_tminus1sq_over3.hpp create mode 100644 include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/miller_loop.hpp create mode 100644 include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp create mode 100644 include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.hpp create mode 100644 test/algebra/curves/plonk/mnt4_g2.cpp create mode 100644 test/algebra/curves/plonk/mnt6_g2.cpp create mode 100644 test/algebra/fields/plonk/non_native/fp2_arithmetic.cpp create mode 100644 test/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.cpp create mode 100644 test/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.cpp create mode 100644 test/algebra/pairing/weierstrass/plonk/mnt4_pairing.cpp diff --git a/cmake/modules b/cmake/modules index a016a4216..5e6b354ea 160000 --- a/cmake/modules +++ b/cmake/modules @@ -1 +1 @@ -Subproject commit a016a42163a58ef68bbde48082d0ddafe68b8e56 +Subproject commit 5e6b354eaa69f6a275992e08220e925c34ba0a19 diff --git a/include/nil/blueprint/components/algebra/curves/detail/plonk/mnt4_g2_point_addition.hpp b/include/nil/blueprint/components/algebra/curves/detail/plonk/mnt4_g2_point_addition.hpp new file mode 100644 index 000000000..fc1b2c3a5 --- /dev/null +++ b/include/nil/blueprint/components/algebra/curves/detail/plonk/mnt4_g2_point_addition.hpp @@ -0,0 +1,385 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for point addition in the elliptic +// curve group G2 = E'(F_p^2) : y^2 = x^3 + a*x + b with +// F_p^2 = F_p[u]/(u^2 - (-1)) and a a non-cubic residue. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_G2_POINT_ADDITION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_G2_POINT_ADDITION_HPP + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + + using mnt4_g2_params = crypto3::algebra::curves::detail:: + mnt4_g2_params<298,crypto3::algebra::curves::forms::short_weierstrass>; + + // E'(F_p^2) : y^2 = x^3 + a*x + b point addition gate. + // Expects point at infinity encoded by (0,0) in input and output + // Input: (xP, yP) = P[4], (xQ, yQ) = Q[4] + // Output: (xR, yR) = R[4], R = P + Q as element of E'(F_p^2) + // + // We organize the computations in 2-cell blocks for storing Fp2 elements + // The 12 blocks used are stored in 1 row with 24 cells or 2 rows with 12 cells. + // Block contents are: + // 0 1 2 3 4 5 6 7 8 9 10 11 + // +--+--+--+--+--+--+---+---+--+--+--+--+ + // |xP|yP|xQ|yQ|zP|zQ|zPQ|wPQ|la| |xR|yR| + // +--+--+--+--+--+--+---+---+--+--+--+--+ + // + + template + class mnt4_g2_point_addition; + + template + class mnt4_g2_point_addition> + : public plonk_component { + + public: + using component_type = plonk_component; + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return mnt4_g2_point_addition::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t lookup_column_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(12,24,12)), // 12 or 24 + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t lookup_column_amount) { + return 1 + (witness_amount < 24); + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), 0); + + struct input_type { + std::array P, Q; + + std::vector> all_vars() { + // std::vector res = {}; + // for(auto & e : P) { res.push_back(e); } + // for(auto & e : Q) { res.push_back(e); } + // return res; + return {P[0], P[1], P[2], P[3], Q[0], Q[1], Q[2], Q[3]}; + } + }; + + struct result_type { + std::array R; + + result_type(const mnt4_g2_point_addition &component, std::uint32_t start_row_index) { + const std::size_t WA = component.witness_amount(); + + for(std::size_t i = 0; i < 4; i++) { + R[i] = var(component.W(WA - 4 + i), start_row_index + (WA < 24), false, var::column_type::witness); + } + } + + std::vector> all_vars() { + std::vector> res = {}; + + for(auto & e : R) { res.push_back(e); } + return res; + } + }; + + template + explicit mnt4_g2_point_addition(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + mnt4_g2_point_addition(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + mnt4_g2_point_addition( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_mnt4_g2_point_addition = + mnt4_g2_point_addition< + crypto3::zk::snark::plonk_constraint_system>; + + template + typename plonk_mnt4_g2_point_addition::result_type generate_assignments( + const plonk_mnt4_g2_point_addition &component, + assignment> + &assignment, + const typename plonk_mnt4_g2_point_addition::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using value_type = typename BlueprintFieldType::value_type; + + const std::size_t WA = component.witness_amount(); + + using policy_type_fp2 = crypto3::algebra::fields::fp2; + using fp2_element = typename policy_type_fp2::value_type; + + fp2_element fp2zero = fp2_element(0,0), + xP = fp2_element(var_value(assignment, instance_input.P[0]), + var_value(assignment, instance_input.P[1])), + yP = fp2_element(var_value(assignment, instance_input.P[2]), + var_value(assignment, instance_input.P[3])), + xQ = fp2_element(var_value(assignment, instance_input.Q[0]), + var_value(assignment, instance_input.Q[1])), + yQ = fp2_element(var_value(assignment, instance_input.Q[2]), + var_value(assignment, instance_input.Q[3])), + zP = yP.inversed(), // NB: division by 0 is defined as 0 + zQ = yQ.inversed(), + zPQ = (xP - xQ).inversed(), + wPQ = (yP + yQ).inversed(), + lambda = (xP == xQ)? ((3*xP.pow(2) + mnt4_g2_params::a ) / (2*yP)) : ((yP-yQ)/(xP-xQ)), + nu = yP - lambda*xP, + xR, yR; + if (yP == fp2zero) { + xR = xQ; + yR = yQ; + } else { + if (yQ == fp2zero) { + xR = xP; + yR = yP; + } else { + if ((xP == xQ) && (yP == -yQ)) { + xR = fp2zero; + yR = fp2zero; + } else { + xR = lambda.pow(2) - xP - xQ, + yR = -(lambda*xR + nu); + } + } + } + + for(std::size_t i = 0; i < 2; i++) { + assignment.witness(component.W(i),start_row_index) = xP.data[i]; + assignment.witness(component.W(2 + i),start_row_index) = yP.data[i]; + assignment.witness(component.W(4 + i),start_row_index) = xQ.data[i]; + assignment.witness(component.W(6 + i),start_row_index) = yQ.data[i]; + assignment.witness(component.W(8 + i),start_row_index) = zP.data[i]; + assignment.witness(component.W(10 + i),start_row_index) = zQ.data[i]; + + assignment.witness(component.W((12 + i) % WA),start_row_index + (WA < 24)) = zPQ.data[i]; + assignment.witness(component.W((14 + i) % WA),start_row_index + (WA < 24)) = wPQ.data[i]; + assignment.witness(component.W((16 + i) % WA),start_row_index + (WA < 24)) = lambda.data[i]; + // block #9 is skipped for alignment purposes + assignment.witness(component.W((20 + i) % WA),start_row_index + (WA < 24)) = xR.data[i]; + assignment.witness(component.W((22 + i) % WA),start_row_index + (WA < 24)) = yR.data[i]; + } + + return typename plonk_mnt4_g2_point_addition::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_mnt4_g2_point_addition &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_mnt4_g2_point_addition::input_type + &instance_input) { + + const std::size_t WA = component.witness_amount(); + + using var = typename plonk_mnt4_g2_point_addition::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + // Fp2 field over constraints: + using fp2_constraint = detail::abstract_fp2_element< + constraint_type, + typename mnt4_g2_params::field_type::value_type >; + + constraint_type cnstr_zero = constraint_type(), + cnstr_one = cnstr_zero + 1; + + constraint_type + constr_a0 = constraint_type()+mnt4_g2_params::a.data[0], + constr_a1 = constraint_type()+mnt4_g2_params::a.data[1]; + + + fp2_constraint one = {cnstr_one,cnstr_zero}, + a = {constr_a0, constr_a1}, + xP = {var(component.W(0), 0, true),var(component.W(1), 0, true)}, + yP = {var(component.W(2), 0, true),var(component.W(3), 0, true)}, + xQ = {var(component.W(4), 0, true),var(component.W(5), 0, true)}, + yQ = {var(component.W(6), 0, true),var(component.W(7), 0, true)}, + zP = {var(component.W(8), 0, true),var(component.W(9), 0, true)}, + zQ = {var(component.W(10), 0, true),var(component.W(11), 0, true)}, + zPQ = {var(component.W(12 % WA), (WA < 24), true),var(component.W(13 % WA), (WA < 24), true)}, + wPQ = {var(component.W(14 % WA), (WA < 24), true),var(component.W(15 % WA), (WA < 24), true)}, + la = {var(component.W(16 % WA), (WA < 24), true),var(component.W(17 % WA), (WA < 24), true)}, + xR = {var(component.W(20 % WA), (WA < 24), true),var(component.W(21 % WA), (WA < 24), true)}, + yR = {var(component.W(22 % WA), (WA < 24), true),var(component.W(23 % WA), (WA < 24), true)}; + fp2_constraint C; + + std::vector Cs = {}; + + // yP(1 - yP zP) = 0 (1) + C = yP * (one - yP * zP); + Cs.push_back(C[0]); Cs.push_back(C[1]); + + // yQ(1 - yQ zQ) = 0 (2) + C = yQ * (one - yQ * zQ); + Cs.push_back(C[0]); Cs.push_back(C[1]); + + // (xR - xQ)(1 - yP zP) = 0 (3) + C = (xR - xQ) * (one - yP * zP); + Cs.push_back(C[0]); Cs.push_back(C[1]); + + // (yR - yQ)(1 - yP zP) = 0 (4) + C = (yR - yQ)*(one - yP * zP); + Cs.push_back(C[0]); Cs.push_back(C[1]); + + // (xR - xP)(1 - yQ zQ) = 0 (5) + C = (xR - xP)*(one - yQ*zQ); + Cs.push_back(C[0]); Cs.push_back(C[1]); + + // (yR - yP)(1 - yQ zQ) = 0 (6) + C = (yR - yP)*(one - yQ*zQ); + Cs.push_back(C[0]); Cs.push_back(C[1]); + + // (xP - xQ)(1 - (xP-xQ) zPQ) = 0 (7) + C = (xP - xQ)*(one - (xP - xQ)*zPQ); + Cs.push_back(C[0]); Cs.push_back(C[1]); + + // zPQ (1 - (xP - xQ) zPQ) = 0 (8) + C = zPQ * (one - (xP - xQ) * zPQ); + Cs.push_back(C[0]); Cs.push_back(C[1]); + + // (yP + yQ)(1 - (yP + yQ) wPQ) = 0 (9) + C = (yP + yQ)*(one - (yP + yQ)*wPQ); + Cs.push_back(C[0]); Cs.push_back(C[1]); + + // wPQ (1 - (yP + yQ) wPQ) = 0 (10) + C = wPQ * (one - (yP + yQ)*wPQ); + Cs.push_back(C[0]); Cs.push_back(C[1]); + + // yP(1 - (xP - xQ) zPQ) yQ(1 - (yP + yQ) wPQ) xR = 0 (11) + C = yP * (one - (xP-xQ)*zPQ) * yQ * (one - (yP + yQ)* wPQ) * xR; + Cs.push_back(C[0]); Cs.push_back(C[1]); + + // yP(1 - (xP - xQ) zPQ) yQ(1 - (yP + yQ) wPQ) yR = 0 (12) + C = yP * (one - (xP-xQ)*zPQ) * yQ * (one - (yP + yQ)* wPQ) * yR; + Cs.push_back(C[0]); Cs.push_back(C[1]); + + // yP yQ (zPQ + wPQ (1 - (xP - xQ) zPQ)) (xR - la^2 + xP + xQ) = 0 (13) + C = yP * yQ * (zPQ + wPQ * (one - (xP - xQ)*zPQ)) * (xR - la*la + xP + xQ); + Cs.push_back(C[0]); Cs.push_back(C[1]); + + // yP yQ (zPQ + wPQ (1 - (xP - xQ) zPQ)) (yR + yP + la(xR - xP)) = 0 (14) + C = yP * yQ * (zPQ + wPQ * (one - (xP - xQ)*zPQ)) * (yR + yP + la*(xR - xP)); + Cs.push_back(C[0]); Cs.push_back(C[1]); + + // yQ ( 2yP zPQ ( (xP - xQ)la - (yP - yQ) ) + (1 - (xP - xQ)zPQ) wPQ (2yP la - 3xP^2 - a)) = 0 (15) + C = yQ * ( + 2*yP * zPQ * ((xP - xQ)*la - (yP - yQ)) + + (one - (xP - xQ)*zPQ) * wPQ *(2*yP*la - 3*xP*xP - a) + ); + Cs.push_back(C[0]); Cs.push_back(C[1]); + + return bp.add_gate(Cs); + } + + template + void generate_copy_constraints( + const plonk_mnt4_g2_point_addition &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_mnt4_g2_point_addition::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_mnt4_g2_point_addition::var; + + const std::size_t WA = component.witness_amount(); + + for(std::size_t i = 0; i < 4; i++) { + bp.add_copy_constraint({var(component.W(i), start_row_index, false), instance_input.P[i]}); + } + } + + template + typename plonk_mnt4_g2_point_addition::result_type generate_circuit( + const plonk_mnt4_g2_point_addition &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_mnt4_g2_point_addition::input_type &instance_input, + const std::size_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, start_row_index); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_mnt4_g2_point_addition::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_G2_POINT_ADDITION_HPP diff --git a/include/nil/blueprint/components/algebra/curves/detail/plonk/mnt6_g2_point_addition.hpp b/include/nil/blueprint/components/algebra/curves/detail/plonk/mnt6_g2_point_addition.hpp new file mode 100644 index 000000000..45c0266a7 --- /dev/null +++ b/include/nil/blueprint/components/algebra/curves/detail/plonk/mnt6_g2_point_addition.hpp @@ -0,0 +1,399 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for point addition in the elliptic +// curve group G2 = E'(F_p^3) : y^2 = x^3 + a * x + b +// F_p^3 = F_p[u]/(u^3 - (a)) and a - non-cubic residue. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_G2_POINT_ADDITION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_G2_POINT_ADDITION_HPP + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + using mnt6_g2_params = crypto3::algebra::curves::detail:: + mnt6_g2_params<298,crypto3::algebra::curves::forms::short_weierstrass>; + + // E'(F_p^2) : y^2 = x^3 + a*x + b point addition gate. + // Expects point at infinity encoded by (0,0) in input and output + // Input: (xP, yP) = P[6], (xQ, yQ) = Q[6] + // Output: (xR, yR) = R[6], R = P + Q as element of E'(F_p^3) + // + // We organize the computations in 3-cell blocks for storing fp3 elements + // The 12 blocks used are stored in 1 row with 36 cells or 2 rows with 18 cells. + // Block contents are: + // 0 1 2 3 4 5 6 7 8 9 10 11 + // +--+--+--+--+--+--+---+---+--+--+--+--+ + // |xP|yP|xQ|yQ|zP|zQ|zPQ|wPQ|la| |xR|yR| + // +--+--+--+--+--+--+---+---+--+--+--+--+ + // + + template + class mnt6_g2_point_addition; + + template + class mnt6_g2_point_addition> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::uint32_t gates_amount() const override { + return mnt6_g2_point_addition::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t lookup_column_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(18,36,18)), // 18 or 36 + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t lookup_column_amount) { + return 1 + (witness_amount < 36); + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), 0); + + struct input_type { + std::array P, Q; + + std::vector> all_vars() { + // std::vector res = {}; + // for(auto & e : P) { res.push_back(e); } + // for(auto & e : Q) { res.push_back(e); } + // return res; + return { + P[0], P[1], P[2], P[3], P[4], P[5], + Q[0], Q[1], Q[2], Q[3], Q[4], Q[5], + }; + } + }; + + struct result_type { + std::array R; + + result_type(const mnt6_g2_point_addition &component, std::uint32_t start_row_index) { + const std::size_t WA = component.witness_amount(); + + for(std::size_t i = 0; i < 6; i++) { + R[i] = var( + component.W(WA - 6 + i), + start_row_index + (WA < 36), + false, + var::column_type::witness); + } + } + + std::vector> all_vars() { + std::vector> res = {}; + + for(auto & e : R) { res.push_back(e); } + return res; + } + }; + + template + explicit mnt6_g2_point_addition(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + mnt6_g2_point_addition(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + mnt6_g2_point_addition( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_mnt6_g2_point_addition = + mnt6_g2_point_addition< + crypto3::zk::snark::plonk_constraint_system>; + + template + typename plonk_mnt6_g2_point_addition::result_type generate_assignments( + const plonk_mnt6_g2_point_addition &component, + assignment> + &assignment, + const typename plonk_mnt6_g2_point_addition::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using value_type = typename BlueprintFieldType::value_type; + + const std::size_t WA = component.witness_amount(); + + using policy_type_fp3 = crypto3::algebra::fields::fp3; + using fp3_element = typename policy_type_fp3::value_type; + + fp3_element fp3zero = fp3_element(0,0,0), + xP = fp3_element(var_value(assignment, instance_input.P[0]), + var_value(assignment, instance_input.P[1]), + var_value(assignment, instance_input.P[2])), + + yP = fp3_element(var_value(assignment, instance_input.P[3]), + var_value(assignment, instance_input.P[4]), + var_value(assignment, instance_input.P[5])), + + xQ = fp3_element(var_value(assignment, instance_input.Q[0]), + var_value(assignment, instance_input.Q[1]), + var_value(assignment, instance_input.Q[2])), + + yQ = fp3_element(var_value(assignment, instance_input.Q[3]), + var_value(assignment, instance_input.Q[4]), + var_value(assignment, instance_input.Q[5])), + zP = yP.inversed(), // NB: division by 0 is defined as 0 + zQ = yQ.inversed(), + zPQ = (xP - xQ).inversed(), + wPQ = (yP + yQ).inversed(), + lambda = (xP == xQ)? ((3*xP.pow(2) + mnt6_g2_params::a )* (2*yP).inversed()) : ((yP-yQ)*zPQ), + nu = yP - lambda*xP, + xR, yR; + if (yP == fp3zero) { + xR = xQ; + yR = yQ; + } else { + if (yQ == fp3zero) { + xR = xP; + yR = yP; + } else { + if ((xP == xQ) && (yP == -yQ)) { + xR = fp3zero; + yR = fp3zero; + } else { + xR = lambda.pow(2) - xP - xQ, + yR = -(lambda*xR + nu); + } + } + } + + for(std::size_t i = 0; i < 3; i++) { + assignment.witness(component.W( 0 + i),start_row_index) = xP.data[i]; + assignment.witness(component.W( 3 + i),start_row_index) = yP.data[i]; + assignment.witness(component.W( 6 + i),start_row_index) = xQ.data[i]; + assignment.witness(component.W( 9 + i),start_row_index) = yQ.data[i]; + assignment.witness(component.W(12 + i),start_row_index) = zP.data[i]; + assignment.witness(component.W(15 + i),start_row_index) = zQ.data[i]; + + assignment.witness(component.W((18 + i) % WA),start_row_index + (WA < 36)) = zPQ.data[i]; + assignment.witness(component.W((21 + i) % WA),start_row_index + (WA < 36)) = wPQ.data[i]; + assignment.witness(component.W((24 + i) % WA),start_row_index + (WA < 36)) = lambda.data[i]; + // block #9 is skipped for alignment purposes + assignment.witness(component.W((30 + i) % WA),start_row_index + (WA < 36)) = xR.data[i]; + assignment.witness(component.W((33 + i) % WA),start_row_index + (WA < 36)) = yR.data[i]; + } + + return typename plonk_mnt6_g2_point_addition::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_mnt6_g2_point_addition &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_mnt6_g2_point_addition::input_type + &instance_input) { + + const std::size_t WA = component.witness_amount(); + + using var = typename plonk_mnt6_g2_point_addition::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + // fp3 field over constraints: + using fp3_constraint = detail::abstract_fp3_element; + + constraint_type cnstr_zero = constraint_type(), + cnstr_one = cnstr_zero + 1; + + constraint_type + constr_a0 = constraint_type()+mnt6_g2_params::a.data[0], + constr_a1 = constraint_type()+mnt6_g2_params::a.data[1], + constr_a2 = constraint_type()+mnt6_g2_params::a.data[2]; + + fp3_constraint one = {cnstr_one, cnstr_zero, cnstr_zero}, + a = {constr_a0, constr_a1, constr_a2}, + xP = { var(component.W( 0 ), 0, true), var(component.W( 1), 0, true), var(component.W( 2), 0, true)}, + yP = { var(component.W( 3 ), 0, true), var(component.W( 4), 0, true), var(component.W( 5), 0, true)}, + xQ = { var(component.W( 6 ), 0, true), var(component.W( 7), 0, true), var(component.W( 8), 0, true)}, + yQ = { var(component.W( 9 ), 0, true), var(component.W(10), 0, true), var(component.W(11), 0, true)}, + zP = { var(component.W(12 ), 0, true), var(component.W(13), 0, true), var(component.W(14), 0, true)}, + zQ = { var(component.W(15 ), 0, true), var(component.W(16), 0, true), var(component.W(17), 0, true)}, + + zPQ = { var(component.W(18 % WA), (WA < 36), true), var(component.W(19 % WA), (WA < 36), true), var(component.W(20 % WA), (WA < 36), true)}, + wPQ = { var(component.W(21 % WA), (WA < 36), true), var(component.W(22 % WA), (WA < 36), true), var(component.W(23 % WA), (WA < 36), true)}, + la = { var(component.W(24 % WA), (WA < 36), true), var(component.W(25 % WA), (WA < 36), true), var(component.W(26 % WA), (WA < 36), true)}, + xR = { var(component.W(30 % WA), (WA < 36), true), var(component.W(31 % WA), (WA < 36), true), var(component.W(32 % WA), (WA < 36), true)}, + yR = { var(component.W(33 % WA), (WA < 36), true), var(component.W(34 % WA), (WA < 36), true), var(component.W(35 % WA), (WA < 36), true)}; + fp3_constraint C; + + std::vector Cs = {}; + + // yP(1 - yP zP) = 0 (1) + C = yP * (one - yP * zP); + Cs.push_back(C[0]); Cs.push_back(C[1]); Cs.push_back(C[2]); + + // yQ(1 - yQ zQ) = 0 (2) + C = yQ * (one - yQ * zQ); + Cs.push_back(C[0]); Cs.push_back(C[1]); Cs.push_back(C[2]); + + // (xR - xQ)(1 - yP zP) = 0 (3) + C = (xR - xQ) * (one - yP * zP); + Cs.push_back(C[0]); Cs.push_back(C[1]); Cs.push_back(C[2]); + + // (yR - yQ)(1 - yP zP) = 0 (4) + C = (yR - yQ)*(one - yP * zP); + Cs.push_back(C[0]); Cs.push_back(C[1]); Cs.push_back(C[2]); + + // (xR - xP)(1 - yQ zQ) = 0 (5) + C = (xR - xP)*(one - yQ*zQ); + Cs.push_back(C[0]); Cs.push_back(C[1]); Cs.push_back(C[2]); + + // (yR - yP)(1 - yQ zQ) = 0 (6) + C = (yR - yP)*(one - yQ*zQ); + Cs.push_back(C[0]); Cs.push_back(C[1]); Cs.push_back(C[2]); + + // (xP - xQ)(1 - (xP-xQ) zPQ) = 0 (7) + C = (xP - xQ)*(one - (xP - xQ)*zPQ); + Cs.push_back(C[0]); Cs.push_back(C[1]); Cs.push_back(C[2]); + + // zPQ (1 - (xP - xQ) zPQ) = 0 (8) + C = zPQ * (one - (xP - xQ) * zPQ); + Cs.push_back(C[0]); Cs.push_back(C[1]); Cs.push_back(C[2]); + + // (yP + yQ)(1 - (yP + yQ) wPQ) = 0 (9) + C = (yP + yQ)*(one - (yP + yQ)*wPQ); + Cs.push_back(C[0]); Cs.push_back(C[1]); Cs.push_back(C[2]); + + // wPQ (1 - (yP + yQ) wPQ) = 0 (10) + C = wPQ * (one - (yP + yQ)*wPQ); + Cs.push_back(C[0]); Cs.push_back(C[1]); Cs.push_back(C[2]); + + // yP(1 - (xP - xQ) zPQ) yQ(1 - (yP + yQ) wPQ) xR = 0 (11) + C = yP * (one - (xP-xQ)*zPQ) * yQ * (one - (yP + yQ)* wPQ) * xR; + Cs.push_back(C[0]); Cs.push_back(C[1]); Cs.push_back(C[2]); + + // yP(1 - (xP - xQ) zPQ) yQ(1 - (yP + yQ) wPQ) yR = 0 (12) + C = yP * (one - (xP-xQ)*zPQ) * yQ * (one - (yP + yQ)* wPQ) * yR; + Cs.push_back(C[0]); Cs.push_back(C[1]); Cs.push_back(C[2]); + + // yP yQ (zPQ + wPQ (1 - (xP - xQ) zPQ)) (xR - la^2 + xP + xQ) = 0 (13) + C = yP * yQ * (zPQ + wPQ * (one - (xP - xQ)*zPQ)) * (xR - la*la + xP + xQ); + Cs.push_back(C[0]); Cs.push_back(C[1]); Cs.push_back(C[2]); + + // yP yQ (zPQ + wPQ (1 - (xP - xQ) zPQ)) (yR + yP + la(xR - xP)) = 0 (14) + C = yP * yQ * (zPQ + wPQ * (one - (xP - xQ)*zPQ)) * (yR + yP + la*(xR - xP)); + Cs.push_back(C[0]); Cs.push_back(C[1]); Cs.push_back(C[2]); + + // yQ ( 2yP zPQ ( (xP - xQ)la - (yP - yQ) ) + (1 - (xP - xQ)zPQ) wPQ (2yP la - 3xP^2)) = 0 (15) + C = yQ * ( + 2*yP * zPQ * ((xP - xQ)*la - (yP - yQ)) + + (one - (xP - xQ)*zPQ) * wPQ *(2*yP*la - 3*xP*xP - a) + ); + Cs.push_back(C[0]); Cs.push_back(C[1]); Cs.push_back(C[2]); + + return bp.add_gate(Cs); + } + + template + void generate_copy_constraints( + const plonk_mnt6_g2_point_addition &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_mnt6_g2_point_addition::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_mnt6_g2_point_addition::var; + + const std::size_t WA = component.witness_amount(); + + for(std::size_t i = 0; i < 6; i++) { + bp.add_copy_constraint({var(component.W(i), start_row_index, false), instance_input.P[i]}); + } + } + + template + typename plonk_mnt6_g2_point_addition::result_type generate_circuit( + const plonk_mnt6_g2_point_addition &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_mnt6_g2_point_addition::input_type &instance_input, + const std::size_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + assignment.enable_selector(selector_index, start_row_index); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_mnt6_g2_point_addition::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_G2_POINT_ADDITION_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp2.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp2.hpp index 7f635a77e..9c67ff754 100644 --- a/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp2.hpp +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp2.hpp @@ -29,14 +29,17 @@ #ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP2_HPP #define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP2_HPP +#include +#include + namespace nil { namespace blueprint { namespace components { namespace detail { template class abstract_fp2_element { - using non_residue = UnderlyingFieldType::non_residue; public: + using policy_type_fp2 = crypto3::algebra::fields::fp2; std::array data; T& operator[](std::size_t idx) { @@ -48,8 +51,8 @@ namespace nil { constexpr abstract_fp2_element operator*(const abstract_fp2_element& other) { - return { data[0] * other.data[0] + non_residue * data[1] * other.data[1], - data[0] * other.data[1] + data[1] * other.data[0]}; + return { data[0] * other[0] + policy_type_fp2::extension_policy::non_residue * data[1] * other[1], + data[0] * other[1] + data[1] * other[0]}; } constexpr abstract_fp2_element operator*(const int x) { return { data[0]*x, data[1]*x }; @@ -58,10 +61,10 @@ namespace nil { return { e[0]*x, e[1]*x }; } constexpr abstract_fp2_element operator+(const abstract_fp2_element& other) { - return { data[0] + other.data[0], data[1] + other.data[1] }; + return { data[0] + other[0], data[1] + other[1] }; } constexpr abstract_fp2_element operator-(const abstract_fp2_element& other) { - return { data[0] - other.data[0], data[1] - other.data[1] }; + return { data[0] - other[0], data[1] - other[1] }; } }; diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp3.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp3.hpp new file mode 100644 index 000000000..2b191b914 --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp3.hpp @@ -0,0 +1,77 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of F_p^3 elements over an abstract entity (to be used with constraints) +// with F_p^3 = F_p[u]/(u^3 + non_residue). +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP3_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP3_HPP + +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + template + class abstract_fp3_element { + public: + std::array data; + + T& operator[](std::size_t idx) { + return data[idx]; + } + const T& operator[](std::size_t idx) const { + return data[idx]; + } + + constexpr abstract_fp3_element operator*(abstract_fp3_element const& other) { + auto s = UnderlyingFieldType::non_residue; + return { + data[0]*other[0] + s*(data[1]*other[2] + data[2]*other[1]), + data[0]*other[1] + data[1]*other[0] + s*data[2]*other[2], + data[0]*other[2] + data[1]*other[1] + data[2]*other[0] + }; + } + + constexpr abstract_fp3_element operator*(const int x) { + return { data[0]*x, data[1]*x, data[2]*x }; + } + friend abstract_fp3_element operator*(const int x, abstract_fp3_element const& e) { + return { e[0]*x, e[1]*x, e[2]*x }; + } + constexpr abstract_fp3_element operator+(abstract_fp3_element const& other) { + return { data[0] + other[0], data[1] + other[1], data[2] + other[2] }; + } + constexpr abstract_fp3_element operator-(abstract_fp3_element const& other) { + return { data[0] - other[0], data[1] - other[1], data[2] - other[2] }; + } + }; + + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP3_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp4.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp4.hpp new file mode 100644 index 000000000..b8602e65d --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp4.hpp @@ -0,0 +1,87 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of F_p^4 elements over an abstract entity (to be used with constraints) +// with F_p^4 = Fp^2 over Fp^2: +// Fp^4 = Fp^2[x]/(x^2 - u), u = (0,1) +// Fp^2 = Fp[y]/(y^2 - v), v = 17 (0x11), u^2 = v +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP4_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP4_HPP + +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + + template + class abstract_fp4_element{ + public: + using policy_type_fp4 = crypto3::algebra::fields::fp4; + std::array x; + + T& operator[](std::size_t idx) { + return x[idx]; + } + const T& operator[](std::size_t idx) const { + return x[idx]; + } + + constexpr abstract_fp4_element operator*(abstract_fp4_element const& y) { + // Devegili et al - Multiplication and squaring in pairing-friendly fields + // https://eprint.iacr.org/2006/471.pdf + constexpr auto s = policy_type_fp4::extension_policy::non_residue; + auto d00 = x[0b00]*y[0b00] + s*(x[0b01]*y[0b01] + x[0b10]*y[0b11] + x[0b11]*y[0b10]); + auto d01 = x[0b00]*y[0b01] + x[0b10]*y[0b10] + x[0b01]*y[0b00] + s*x[0b11]*y[0b11]; + auto d10 = x[0b00]*y[0b10] + x[0b10]*y[0b00] + s*(x[0b01]*y[0b11] + x[0b11]*y[0b01]); + auto d11 = x[0b00]*y[0b11] + x[0b01]*y[0b10] + x[0b10]*y[0b01] + x[0b11]*y[0b00]; + + return { d00, d01, d10, d11}; + } + + constexpr abstract_fp4_element operator*(const int a) { + return { x[0]*a, x[1]*a, x[2]*a, x[3]*a }; + } + friend abstract_fp4_element operator*(const int a, abstract_fp4_element const& x) { + return { x[0]*a, x[1]*a, x[2]*a, x[3]*a }; + } + constexpr abstract_fp4_element operator+(abstract_fp4_element const& y) { + return { x[0] + y[0], x[1] + y[1], x[2] + y[2], x[3] + y[3] }; + } + constexpr abstract_fp4_element operator-(abstract_fp4_element const& y) { + return { x[0] - y[0], x[1] - y[1], x[2] - y[2], x[3] - y[3] }; + } + }; + + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_abstract_FP4_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/mnt4_fp4.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/mnt4_fp4.hpp new file mode 100644 index 000000000..e15202d82 --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/mnt4_fp4.hpp @@ -0,0 +1,86 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of F_p^4 elements over an mnt4 entity (to be used with constraints) +// with F_p^4 = Fp^2 over Fp^2: +// Fp^4 = Fp^2[x]/(x^2 - u), u = (0,1) +// Fp^2 = Fp[y]/(y^2 - v), v = 17 (0x11), u^2 = v +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_FP4_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_FP4_HPP + +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + + template + class mnt4_fp4_element { + public: + std::array x; + + T& operator[](std::size_t idx) { + return x[idx]; + } + const T& operator[](std::size_t idx) const { + return x[idx]; + } + + constexpr mnt4_fp4_element operator*(mnt4_fp4_element const& y) { + + // Devegili et al - Multiplication and squaring in pairing-friendly fields + // https://eprint.iacr.org/2006/471.pdf + //crypto3::algebra::curves::mnt4<298>::..?..::non_residue; + constexpr auto s = 0x11; + auto d00 = x[0b00]*y[0b00] + s*(x[0b01]*y[0b01] + x[0b10]*y[0b11] + x[0b11]*y[0b10]); + auto d01 = x[0b00]*y[0b01] + x[0b10]*y[0b10] + x[0b01]*y[0b00] + s*x[0b11]*y[0b11]; + auto d10 = x[0b00]*y[0b10] + x[0b10]*y[0b00] + s*(x[0b01]*y[0b11] + x[0b11]*y[0b01]); + auto d11 = x[0b00]*y[0b11] + x[0b01]*y[0b10] + x[0b10]*y[0b01] + x[0b11]*y[0b00]; + + return { d00, d01, d10, d11}; + } + + constexpr mnt4_fp4_element operator*(const int a) { + return { x[0]*a, x[1]*a, x[2]*a, x[3]*a }; + } + friend mnt4_fp4_element operator*(const int a, mnt4_fp4_element const& x) { + return { x[0]*a, x[1]*a, x[2]*a, x[3]*a }; + } + constexpr mnt4_fp4_element operator+(mnt4_fp4_element const& y) { + return { x[0] + y[0], x[1] + y[1], x[2] + y[2], x[3] + y[3] }; + } + constexpr mnt4_fp4_element operator-(mnt4_fp4_element const& y) { + return { x[0] - y[0], x[1] - y[1], x[2] - y[2], x[3] - y[3] }; + } + }; + + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_FP4_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/mnt6_fp6.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/mnt6_fp6.hpp new file mode 100644 index 000000000..56b2d115a --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/mnt6_fp6.hpp @@ -0,0 +1,100 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of F_p^6 elements over an mnt6 entity (to be used with constraints) +// with F_p^6 = Fp^2 over Fp^3: +// Fp^6 = Fp^2[x]/(x^2 - u), u^2 = v = (0, 1, 0) +// Fp^3 = Fp[y]/(y^3 - v), v^3 = 5 +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_FP6_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_FP6_HPP + +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + + template + class mnt6_fp6_element { + public: + std::array x; + + T& operator[](std::size_t idx) { + return x[idx]; + } + const T& operator[](std::size_t idx) const { + return x[idx]; + } + + constexpr mnt6_fp6_element operator*(mnt6_fp6_element const& y) { + + //crypto3::algebra::curves::mnt4<298>::..?..::non_residue; + constexpr int + _00 = 0, _01 = 1, _02 = 2, + _10 = 3, _11 = 4, _12 = 5; + + constexpr auto s = 0x5; + auto d00 = x[0b00]*y[0b00] + s*(x[0b01]*y[0b01] + x[0b10]*y[0b11] + x[0b11]*y[0b10]); + auto d01 = x[0b01]*y[0b10] + x[0b00]*y[0b11] + x[0b10]*y[0b10] + s*x[0b11]*y[0b11]; + auto d10 = x[0b00]*y[0b10] + x[0b10]*y[0b00] + s*(x[0b01]*y[0b11] + x[0b11]*y[0b01]); + auto d11 = x[0b00]*y[0b11] + x[0b01]*y[0b10] + x[0b10]*y[0b01] + x[0b11]*y[0b00]; + + return { d00, d01, d10, d11}; + } + + constexpr mnt6_fp6_element operator*(const int a) { + return { + x[0]*a, x[1]*a, x[2]*a, + x[3]*a, x[4]*a, x[5]*a, + }; + } + friend mnt6_fp6_element operator*(const int a, mnt6_fp6_element const& x) { + return { + x[0]*a, x[1]*a, x[2]*a, + x[3]*a, x[4]*a, x[5]*a, + }; + } + constexpr mnt6_fp6_element operator+(mnt6_fp6_element const& y) { + return { + x[0] + y[0], x[1] + y[1], x[2] + y[2], + x[3] + y[3], x[4] + y[4], x[5] + y[5], + }; + } + constexpr mnt6_fp6_element operator-(mnt6_fp6_element const& y) { + return { + x[0] - y[0], x[1] - y[1], x[2] - y[2], + x[3] - y[3], x[4] - y[4], x[5] - y[5], + }; + } + }; + + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_FP6_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp new file mode 100644 index 000000000..ba59aacb3 --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp @@ -0,0 +1,510 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for F_p^{4} raising to a fixed power +// t, which is a parameter of the component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_FP4_FIXED_POWER_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_FP4_FIXED_POWER_HPP + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + + // + // Component for raising to a fixed power t in F_p^4 + // Input: x[4] + // Output: y[4]: y = x^t as elements of F_p^4 + // + + template + class mnt4_fp4_fixed_power; + + template + class mnt4_fp4_fixed_power, BlueprintFieldType> + : public plonk_component + { + public: + using component_type = plonk_component; + using var = typename component_type::var; + + using constraint_type = crypto3::zk::snark::plonk_constraint; + using policy_type = crypto3::algebra::fields::fp4; + using integral_type = typename BlueprintFieldType::integral_type; + using extended_integral_type = typename BlueprintFieldType::extended_integral_type; + using fp4_element = typename policy_type::value_type; + using fp4_constraint = detail::mnt4_fp4_element; + + private: + static std::vector base4(extended_integral_type x) { + if (x > 0) { + std::vector res = {std::uint8_t(x % 4)}; + x /= 4; + while (x > 0) { + res.insert(res.begin(), std::uint8_t(x % 4)); + x /= 4; + } + return res; + } else { + return {0}; + } + } + + static std::size_t gates_amount_internal(extended_integral_type power) { + std::size_t gates = 1; // at least one for power-4 operations + std::vector exp_plan = base4(power); + if (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1) { + gates++; // a multiplication gate + } + gates += (std::count(exp_plan.begin(),exp_plan.end(),3) > 0); // a cubing gate + gates += (std::count(exp_plan.begin(),exp_plan.end(),2) > 0); // a squaring gate + + return gates; + } + + public: + using manifest_type = plonk_component_manifest; + + const extended_integral_type power/* = pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0*/; + + const std::vector exp_plan, exp_precompute; + const std::size_t rows_amount; + + class gate_manifest_type : public component_gate_manifest { + const extended_integral_type power; + std::array gates_footprint(extended_integral_type power) const { + std::vector exp_plan = base4(power); + return { (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1), + (std::count(exp_plan.begin(),exp_plan.end(),3) > 0), + (std::count(exp_plan.begin(),exp_plan.end(),2) > 0) }; + } + public: + gate_manifest_type(extended_integral_type power) : power(power) {} + + std::uint32_t gates_amount() const override { + return mnt4_fp4_fixed_power::gates_amount_internal(power); + } + + bool operator<(const component_gate_manifest *other) const override { + extended_integral_type o_power = dynamic_cast(other)->power; + + std::array + gates = gates_footprint(power), + o_gates = gates_footprint(o_power); + return (gates < o_gates); + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount, + std::size_t lookup_column_amount, + extended_integral_type power) + { + static gate_manifest manifest = gate_manifest(gate_manifest_type(power)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(4, 300, 1)), + false + ); + return manifest; + } + + static std::vector get_precomputed_exps(const std::vector exps) + { + std::vector precompute = {exps[0]}; + if ((exps[0] != 3) && (std::count(exps.begin(),exps.end(),3) > 0)) { + precompute.insert(precompute.begin(),3); + } + if ((exps[0] != 2) && (std::count(exps.begin(),exps.end(),2) > 0)) { + precompute.insert(precompute.begin(),2); + } + return precompute; + } + + static std::size_t get_rows_amount(extended_integral_type power) + { + std::vector + exp_plan = base4(power), + exp_precompute = get_precomputed_exps(exp_plan); + + std::size_t rows = 0; + + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + rows += 1 + (exp_precompute[i] > 1); + } + for(std::size_t i = 1; i < exp_plan.size(); i++) { + rows += 1 + 2*(exp_plan[i] > 0); + } + return rows; + } + + struct input_type { + std::array x; + + std::vector> all_vars() { + return {x[0], x[1], x[2], x[3]}; + } + }; + + struct result_type { + std::array output; + + result_type(mnt4_fp4_fixed_power const& component, std::uint32_t start_row_index) + { + std::size_t last_row = start_row_index + component.rows_amount - 1; + + for(std::size_t i = 0; i < output.size(); i++) { + output[i] = var(component.W(i), last_row, false, var::column_type::witness); + } + } + + std::vector> all_vars() + { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template explicit + mnt4_fp4_fixed_power( + ContainerType witness, + extended_integral_type power) : + component_type(witness, {}, {}, get_manifest()), + power(power), + exp_plan(base4(power)), + exp_precompute(get_precomputed_exps(exp_plan)), + rows_amount(get_rows_amount(power)) + { }; + + template + mnt4_fp4_fixed_power( + WitnessContainerType witness, + ConstantContainerType constant, + PublicInputContainerType public_input, + extended_integral_type power) : + component_type(witness, constant, public_input, get_manifest()), + power(power), + exp_plan(base4(power)), + exp_precompute(get_precomputed_exps(exp_plan)), + rows_amount(get_rows_amount(power)) + { }; + + mnt4_fp4_fixed_power( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list public_inputs, + extended_integral_type power) : + component_type(witnesses, constants, public_inputs, get_manifest()), + power(power), + exp_plan(base4(power)), + exp_precompute(get_precomputed_exps(exp_plan)), + rows_amount(get_rows_amount(power)) + { }; + }; + + /* */ + + template + using component_type = mnt4_fp4_fixed_power, BlueprintFieldType>; + + template + typename component_type::result_type + generate_assignments( + component_type const& component, + assignment> & assignment, + typename component_type::input_type const& instance_input, + const std::uint32_t start_row_index) + { + + using value_type = typename BlueprintFieldType::value_type; + using policy_type = typename component_type::policy_type; + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + std::array::policy_type::arity> x; + + for(std::size_t i = 0; i < x.size(); i++) { + x[i] = var_value(assignment, instance_input.x[i]); + } + + using fp4_element = typename component_type::fp4_element; + fp4_element X = fp4_element({ {x[0],x[1]}, {x[2],x[3]},}), Y = X; + + std::size_t row = 0; + + auto fill_row = [&component, &assignment, &start_row_index, &row](fp4_element const& V) { + value_type d00 = V.data[0].data[0]; + value_type d01 = V.data[0].data[1]; + value_type d10 = V.data[1].data[0]; + value_type d11 = V.data[1].data[1]; + assignment.witness(component.W(0),start_row_index + row) = d00; + assignment.witness(component.W(1),start_row_index + row) = d01; + assignment.witness(component.W(2),start_row_index + row) = d10; + assignment.witness(component.W(3),start_row_index + row) = d11; + + /* + for(std::size_t i = 0; i < policy_type::arity; i++) { + assignment.witness(component.W(i),start_row_index + row) = V.data[i/2].data[i % 2]; + } + */ + row++; + }; + + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + fill_row(X); // X + if (exp_precompute[i] > 1) { + fill_row(X.pow(exp_precompute[i])); + } + } + + Y = X.pow(exp_plan[0]); + for(std::size_t i = 1; i < exp_plan.size(); i++) { + Y = Y.pow(4); fill_row(Y); // every step includes a power-4 operation + if (exp_plan[i] > 0) { // for every non-zero digit we need a multiplication too + fill_row(X.pow(exp_plan[i])); + Y = Y * X.pow(exp_plan[i]); fill_row(Y); + } + } + + return typename component_type::result_type( + component, start_row_index); + } + + template + std::vector generate_gates( + component_type const& component, + circuit> & bp, + assignment> const& assignment, + typename component_type::input_type const& instance_input) + { + using var = typename component_type::var; + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + std::vector gate_list = {}; // at most 4 gate ids + + + using policy_type = typename component_type::policy_type; + typename component_type::fp4_constraint X, Y, Z, C; + + // power-4 gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = (X * X) * (X * X); + + using constraint_type = typename component_type::constraint_type; + + std::vector pow4_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + pow4_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(pow4_constrs)); + + if (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1) { + // at least one digit besides the first is non-zero + // => we need a multiplication gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + Z[i] = var(component.W(i), 1, true); + } + C = X * Y; + + std::vector mult_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + mult_constrs.push_back(C[i] - Z[i]); + } + gate_list.push_back(bp.add_gate(mult_constrs)); + } + + if (std::count(exp_precompute.begin(),exp_precompute.end(),3)) { + // we need a cubing gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = X * X * X; + + std::vector cube_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + cube_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(cube_constrs)); + } + + if (std::count(exp_precompute.begin(),exp_precompute.end(),2)) { + // we need a squaring gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = X * X; + + std::vector square_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + square_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(square_constrs)); + } + + return gate_list; + } + + template + void generate_copy_constraints( + component_type const& component, + circuit> &bp, + assignment> &assignment, + const typename component_type::input_type &instance_input, + const std::size_t start_row_index) + { + using var = typename component_type::var; + using policy_type = typename component_type::policy_type; + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + // for storing relative ids of rows where x^0, x^1, x^2 and x^3 are stored + std::array row_of_power = {0,1,0,0}; + + for(std::size_t small_power = 3; small_power > 0; small_power--) { + if (std::count(exp_precompute.begin(),exp_precompute.end(),small_power)) { + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + if (exp_precompute[i] == small_power) { + // this gives a wrong value for small_power = 1, but it is coherent with the next part + row_of_power[small_power] = 2*i + 1; + } + } + } + } + + // copies of initial data + for(std::size_t j = 1; j < 4; j++) { + if (row_of_power[j] > 0) { // => we need a copy of initial data before row_of_power[j] + for(std::size_t i = 0; i < policy_type::arity; i++) { + bp.add_copy_constraint({var(component.W(i), start_row_index + row_of_power[j]-1, false), instance_input.x[i]}); + } + } + } + row_of_power[1] = 0; // from now on we need the real row number for where x^1 is stored + + std::size_t row = 0; + for(std::size_t j = 0; j < exp_precompute.size(); j++) { + row += 1 + (exp_precompute[j] > 1); // for x² and x³ skip 2 rows, for x just one + } + for(std::size_t j = 1; j < exp_plan.size(); j++) { + row++; // skip the power-4 value + if (exp_plan[j] > 0) { // this row has a copy of some precomputed row + for(std::size_t i = 0; i < policy_type::arity; i++) { + bp.add_copy_constraint({ + var(component.W(i), start_row_index + row_of_power[exp_plan[j]], false), + var(component.W(i), start_row_index + row, false) + }); + } + row += 2; + } + } + } + + template + typename component_type::result_type + generate_circuit( + component_type const& component, + circuit>& bp, + assignment>& assignment, + typename component_type::input_type const& instance_input, + const std::size_t start_row_index) + { + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + std::size_t row = 0; + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + row += 1 + (exp_precompute[i] > 1); // for x² and x³ skip 2 rows, for x just one + } + for(std::size_t i = 1; i < exp_plan.size(); i++) { + assignment.enable_selector(selector_index[0], start_row_index + row); // power-4 gate + row++; + if (exp_plan[i] > 0) { + assignment.enable_selector(selector_index[1], start_row_index + row); // multiplication gate + row += 2; + } + } + // did we even use a multiplication gate? + std::size_t gate_id = 1 + (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1); + + // cubing and squaring gates if we need either of them + for(std::size_t small_power = 3; small_power > 1; small_power--) { + if (std::count(exp_precompute.begin(),exp_precompute.end(),small_power)) { + std::size_t row_of_power; + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + if (exp_precompute[i] == small_power) { + row_of_power = 2*i + 1; + } + } + assignment.enable_selector(selector_index[gate_id], start_row_index + row_of_power); + gate_id++; + } + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename component_type::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_FP4_FIXED_POWER_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp new file mode 100644 index 000000000..1ede12082 --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp @@ -0,0 +1,495 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for F_p^{6} raising to a fixed power +// t, which is a parameter of the component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_FP6_FIXED_POWER_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_FP6_FIXED_POWER_HPP + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + + // + // Component for raising to a fixed power t in F_p^6 (fp6_2over3) + // Input: x[6] + // Output: y[6]: y = x^t as elements of F_p^6 + // + + template + class mnt6_fp6_fixed_power; + + template + class mnt6_fp6_fixed_power> + : public plonk_component + { + public: + using component_type = plonk_component; + using var = typename component_type::var; + + using constraint_type = crypto3::zk::snark::plonk_constraint; + using policy_type = crypto3::algebra::fields::fp6_2over3; + using integral_type = typename policy_type::extension_policy::integral_type; + using fp6_element = typename policy_type::value_type; + using fp6_constraint = detail::mnt6_fp6_element; + + private: + static std::vector base4(integral_type x) { + if (x > 0) { + std::vector res = {std::uint8_t(x % 4)}; + x /= 4; + while (x > 0) { + res.insert(res.begin(), std::uint8_t(x % 4)); + x /= 4; + } + return res; + } else { + return {0}; + } + } + + static std::size_t gates_amount_internal(integral_type power) { + std::size_t gates = 1; // at least one for power-4 operations + std::vector exp_plan = base4(power); + if (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1) { + gates++; // a multiplication gate + } + gates += (std::count(exp_plan.begin(),exp_plan.end(),3) > 0); // a cubing gate + gates += (std::count(exp_plan.begin(),exp_plan.end(),2) > 0); // a squaring gate + + return gates; + } + + public: + using manifest_type = plonk_component_manifest; + + const integral_type power/* = pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0*/; + + const std::vector exp_plan, exp_precompute; + const std::size_t rows_amount; + + class gate_manifest_type : public component_gate_manifest { + std::array gates_footprint(integral_type power) const { + std::vector exp_plan = base4(power); + return { (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1), + (std::count(exp_plan.begin(),exp_plan.end(),3) > 0), + (std::count(exp_plan.begin(),exp_plan.end(),2) > 0) }; + } + public: + const integral_type power; + gate_manifest_type(integral_type power) : power(power) {} + + std::uint32_t gates_amount() const override { + return mnt6_fp6_fixed_power::gates_amount_internal(power); + } + + bool operator<(const component_gate_manifest *other) const override { + integral_type o_power = dynamic_cast(other)->power; + + std::array + gates = gates_footprint(power), + o_gates = gates_footprint(o_power); + return (gates < o_gates); + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount, + std::size_t lookup_column_amount, + integral_type power) + { + static gate_manifest manifest = gate_manifest(gate_manifest_type(power)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(policy_type::arity)), // ?? + false + ); + return manifest; + } + + static std::vector get_precomputed_exps(const std::vector exps) + { + std::vector precompute = {exps[0]}; + if ((exps[0] != 3) && (std::count(exps.begin(),exps.end(),3) > 0)) { + precompute.insert(precompute.begin(),3); + } + if ((exps[0] != 2) && (std::count(exps.begin(),exps.end(),2) > 0)) { + precompute.insert(precompute.begin(),2); + } + return precompute; + } + + static std::size_t get_rows_amount( + std::size_t witness_amount, + std::size_t lookup_column_amount, + integral_type power) + { + std::vector + exp_plan = base4(power), + exp_precompute = get_precomputed_exps(exp_plan); + + std::size_t rows = 0; + + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + rows += 1 + (exp_precompute[i] > 1); + } + for(std::size_t i = 1; i < exp_plan.size(); i++) { + rows += 1 + 2*(exp_plan[i] > 0); + } + return rows; + } + + struct input_type { + std::array x; + + std::vector> all_vars() { + return {x[0], x[1], x[2], x[3], x[4], x[5]}; + } + }; + + struct result_type { + std::array output; + + result_type(mnt6_fp6_fixed_power const& component, std::uint32_t start_row_index) + { + std::size_t last_row = start_row_index + component.rows_amount - 1; + + for(std::size_t i = 0; i < output.size(); i++) { + output[i] = var(component.W(i), last_row, false, var::column_type::witness); + } + } + + std::vector> all_vars() + { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template explicit + mnt6_fp6_fixed_power( + ContainerType witness, + integral_type power) : + component_type(witness, {}, {}, get_manifest()), + power(power), + exp_plan(base4(power)), + exp_precompute(get_precomputed_exps(exp_plan)), + rows_amount(get_rows_amount(this->witness_amount(), 0, power)) + { }; + + template + mnt6_fp6_fixed_power( + WitnessContainerType witness, + ConstantContainerType constant, + PublicInputContainerType public_input, + integral_type power) : + component_type(witness, constant, public_input, get_manifest()), + power(power), + exp_plan(base4(power)), + exp_precompute(get_precomputed_exps(exp_plan)), + rows_amount(get_rows_amount(this->witness_amount(), 0, power)) + { }; + + mnt6_fp6_fixed_power( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list public_inputs, + integral_type power) : + component_type(witnesses, constants, public_inputs, get_manifest()), + power(power), + exp_plan(base4(power)), + exp_precompute(get_precomputed_exps(exp_plan)), + rows_amount(get_rows_amount(this->witness_amount(), 0, power)) + { }; + }; + + /* */ + + template + typename mnt6_fp6_fixed_power::result_type + generate_assignments( + mnt6_fp6_fixed_power const& component, + assignment> const& assignment, + typename mnt6_fp6_fixed_power::input_type const& instance_input, + const std::uint32_t start_row_index) + { + + using value_type = typename BlueprintFieldType::value_type; + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + std::array::policy_type::arity> x; + + for(std::size_t i = 0; i < x.size(); i++) { + x[i] = var_value(assignment, instance_input.x[i]); + } + + using fp6_element = typename mnt6_fp6_fixed_power::fp6_element; + fp6_element X = fp6_element({ {x[0],x[1], x[2]}, {x[3],x[4]},x[5]}), Y = X; + + std::size_t row = 0; + + auto fill_row = [&component, &assignment, &start_row_index, &row](fp6_element V) { + for(std::size_t i = 0; i < V.arity; i++) { + assignment.witness(component.W(i),start_row_index + row) = V.data[i/3].data[i % 3]; + } + row++; + }; + + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + fill_row(X); // X + if (exp_precompute[i] > 1) { + fill_row(X.pow(exp_precompute[i])); + } + } + + Y = X.pow(exp_plan[0]); + for(std::size_t i = 1; i < exp_plan.size(); i++) { + Y = Y.pow(4); fill_row(Y); // every step includes a power-4 operation + if (exp_plan[i] > 0) { // for every non-zero digit we need a multiplication too + fill_row(X.pow(exp_plan[i])); + Y = Y * X.pow(exp_plan[i]); fill_row(Y); + } + } + + return typename mnt6_fp6_fixed_power::result_type( + component, start_row_index); + } + + template + std::vector generate_gates( + mnt6_fp6_fixed_power const& component, + circuit> & bp, + assignment> const& assignment, + typename mnt6_fp6_fixed_power::input_type const& instance_input) + { + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + std::vector gate_list = {}; // at most 4 gate ids + + + using policy_type = typename mnt6_fp6_fixed_power::policy_type; + typename mnt6_fp6_fixed_power::fp4_constraint X, Y, Z, C; + + // power-4 gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = (X * X) * (X * X); + + using constraint_type = typename mnt6_fp6_fixed_power::constraint_type; + + std::vector pow4_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + pow4_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(pow4_constrs)); + + if (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1) { + // at least one digit besides the first is non-zero + // => we need a multiplication gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + Z[i] = var(component.W(i), 1, true); + } + C = X * Y; + + std::vector mult_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + mult_constrs.push_back(C[i] - Z[i]); + } + gate_list.push_back(bp.add_gate(mult_constrs)); + } + + if (std::count(exp_precompute.begin(),exp_precompute.end(),3)) { + // we need a cubing gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = X * X * X; + + std::vector cube_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + cube_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(cube_constrs)); + } + + if (std::count(exp_precompute.begin(),exp_precompute.end(),2)) { + // we need a squaring gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = X * X; + + std::vector square_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + square_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(square_constrs)); + } + return gate_list; + } + + template + void generate_copy_constraints( + mnt6_fp6_fixed_power const& component, + circuit> &bp, + assignment> &assignment, + const typename mnt6_fp6_fixed_power::input_type &instance_input, + const std::size_t start_row_index) + { + using var = typename mnt6_fp6_fixed_power::var; + using policy_type = typename mnt6_fp6_fixed_power::policy_type; + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + // for storing relative ids of rows where x^0, x^1, x^2 and x^3 are stored + std::array row_of_power = {0,1,0,0}; + + for(std::size_t small_power = 3; small_power > 0; small_power--) { + if (std::count(exp_precompute.begin(),exp_precompute.end(),small_power)) { + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + if (exp_precompute[i] == small_power) { + // this gives a wrong value for small_power = 1, but it is coherent with the next part + row_of_power[small_power] = 2*i + 1; + } + } + } + } + + // copies of initial data + for(std::size_t j = 1; j < 4; j++) { + if (row_of_power[j] > 0) { // => we need a copy of initial data before row_of_power[j] + for(std::size_t i = 0; i < policy_type::arity; i++) { + bp.add_copy_constraint({var(component.W(i), start_row_index + row_of_power[j]-1, false), instance_input.x[i]}); + } + } + } + row_of_power[1] = 0; // from now on we need the real row number for where x^1 is stored + + std::size_t row = 0; + for(std::size_t j = 0; j < exp_precompute.size(); j++) { + row += 1 + (exp_precompute[j] > 1); // for x² and x³ skip 2 rows, for x just one + } + for(std::size_t j = 1; j < exp_plan.size(); j++) { + row++; // skip the power-4 value + if (exp_plan[j] > 0) { // this row has a copy of some precomputed row + for(std::size_t i = 0; i < policy_type::arity; i++) { + bp.add_copy_constraint({ + var(component.W(i), start_row_index + row_of_power[exp_plan[j]], false), + var(component.W(i), start_row_index + row, false) + }); + } + row += 2; + } + } + } + + template + typename mnt6_fp6_fixed_power::result_type + generate_circuit( + mnt6_fp6_fixed_power const& component, + circuit>& bp, + assignment>& assignment, + typename mnt6_fp6_fixed_power::input_type const& instance_input, + const std::size_t start_row_index) + { + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + std::size_t row = 0; + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + row += 1 + (exp_precompute[i] > 1); // for x² and x³ skip 2 rows, for x just one + } + for(std::size_t i = 1; i < exp_plan.size(); i++) { + assignment.enable_selector(selector_index[0], start_row_index + row); // power-4 gate + row++; + if (exp_plan[i] > 0) { + assignment.enable_selector(selector_index[1], start_row_index + row); // multiplication gate + row += 2; + } + } + // did we even use a multiplication gate? + std::size_t gate_id = 1 + (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1); + + // cubing and squaring gates if we need either of them + for(std::size_t small_power = 3; small_power > 1; small_power--) { + if (std::count(exp_precompute.begin(),exp_precompute.end(),small_power)) { + std::size_t row_of_power; + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + if (exp_precompute[i] == small_power) { + row_of_power = 2*i + 1; + } + } + assignment.enable_selector(selector_index[gate_id], start_row_index + row_of_power); + gate_id++; + } + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename mnt6_fp6_fixed_power::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_FP6_FIXED_POWER_HPP diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/bls12_381_pairing.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/bls12_381_pairing.hpp new file mode 100644 index 000000000..22e3a91ea --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/bls12_381_pairing.hpp @@ -0,0 +1,295 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of the BLS12-381 pairing component +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BLS12_381_PAIRING_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BLS12_381_PAIRING_HPP + +#include + +#include +#include +#include +#include + +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + // + // Component for computing the pairing of + // two points: P from E(F_p) and Q from E'(F_p^2) + // for BLS12-381. + // Input: P[2], Q[4] ( we assume P and Q are NOT (0,0), i.e. not the points at infinity, NOT CHECKED ) + // Output: f[12]: an element of F_p^12 + // + // It is just the Miller loop followed by exponentiation. + // We realize the circuit in two versions - 12-column and 24-column. + // + + using namespace detail; + + template + class bls12_381_pairing; + + template + class bls12_381_pairing> + : public plonk_component { + + static std::size_t gates_amount_internal(std::size_t witness_amount) { + return 0; + } + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + using bls12_miller_loop_type = miller_loop>; + using bls12_exponentiation_type = bls12_exponentiation>; + + class gate_manifest_type : public component_gate_manifest { + public: + std::size_t witness_amount; + + gate_manifest_type(std::size_t witness_amount_) : witness_amount(witness_amount_) {} + + std::uint32_t gates_amount() const override { + return bls12_381_pairing::gates_amount_internal(witness_amount); + } + + bool operator<(const component_gate_manifest *other) const override { + return (witness_amount < dynamic_cast(other)->witness_amount); + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t lookup_column_amount) { + gate_manifest manifest = + gate_manifest(gate_manifest_type(witness_amount)) + .merge_with(bls12_miller_loop_type::get_gate_manifest(witness_amount,lookup_column_amount)) + .merge_with(bls12_exponentiation_type::get_gate_manifest(witness_amount,lookup_column_amount)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(12,24,12)), // 12 or 24 + false + ).merge_with(bls12_miller_loop_type::get_manifest()) + .merge_with(bls12_exponentiation_type::get_manifest()); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t lookup_column_amount) { + return bls12_miller_loop_type::get_rows_amount(witness_amount, lookup_column_amount,0xD201000000010000) + + bls12_exponentiation_type::get_rows_amount(witness_amount, lookup_column_amount); + } + + const std::size_t gates_amount = gates_amount_internal(this->witness_amount()); + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), 0); + + struct input_type { + std::array P; + std::array Q; + + std::vector> all_vars() { + return {P[0], P[1], Q[0], Q[1], Q[2], Q[3]}; + } + }; + + struct result_type { + std::array output; + + std::vector> all_vars() { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template + explicit bls12_381_pairing(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) { }; + + template + bls12_381_pairing(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) { }; + bls12_381_pairing( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) { }; + }; + + template + using plonk_bls12_381_pairing = + bls12_381_pairing>; + + template + typename plonk_bls12_381_pairing::result_type generate_assignments( + const plonk_bls12_381_pairing &component, + assignment> + &assignment, + const typename plonk_bls12_381_pairing::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_bls12_381_pairing; + using bls12_miller_loop_type = typename component_type::bls12_miller_loop_type; + using bls12_exponentiation_type = typename component_type::bls12_exponentiation_type; + + bls12_miller_loop_type miller_loop_instance( component._W, component._C, component._PI, 0xD201000000010000); + bls12_exponentiation_type exponentiation_instance( component._W, component._C, component._PI); + + typename bls12_miller_loop_type::input_type miller_loop_input = {instance_input.P, instance_input.Q}; + typename bls12_miller_loop_type::result_type miller_loop_result = + generate_assignments(miller_loop_instance, assignment, miller_loop_input, start_row_index); + + typename bls12_exponentiation_type::input_type exponentiation_input = { + miller_loop_result.output[0], + miller_loop_result.output[1], + miller_loop_result.output[2], + miller_loop_result.output[3], + miller_loop_result.output[4], + miller_loop_result.output[5], + miller_loop_result.output[6], + miller_loop_result.output[7], + miller_loop_result.output[8], + miller_loop_result.output[9], + miller_loop_result.output[10], + miller_loop_result.output[11] + }; + typename bls12_exponentiation_type::result_type exp_result = + generate_assignments(exponentiation_instance, assignment, exponentiation_input, + start_row_index + miller_loop_instance.rows_amount); + + typename plonk_bls12_381_pairing::result_type res = { + exp_result.output[0], + exp_result.output[1], + exp_result.output[2], + exp_result.output[3], + exp_result.output[4], + exp_result.output[5], + exp_result.output[6], + exp_result.output[7], + exp_result.output[8], + exp_result.output[9], + exp_result.output[10], + exp_result.output[11] + }; + return res; + } + + template + std::vector generate_gates( + const plonk_bls12_381_pairing &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bls12_381_pairing::input_type + &instance_input) { + return {}; + } + + template + void generate_copy_constraints( + const plonk_bls12_381_pairing &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bls12_381_pairing::input_type &instance_input, + const std::size_t start_row_index) { + } + + template + typename plonk_bls12_381_pairing::result_type generate_circuit( + const plonk_bls12_381_pairing &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bls12_381_pairing::input_type &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_bls12_381_pairing; + using bls12_miller_loop_type = typename component_type::bls12_miller_loop_type; + using bls12_exponentiation_type = typename component_type::bls12_exponentiation_type; + + bls12_miller_loop_type miller_loop_instance( component._W, component._C, component._PI, 0xD201000000010000); + bls12_exponentiation_type exponentiation_instance( component._W, component._C, component._PI); + + typename bls12_miller_loop_type::input_type miller_loop_input = {instance_input.P, instance_input.Q}; + typename bls12_miller_loop_type::result_type miller_loop_result = + generate_circuit(miller_loop_instance, bp, assignment, miller_loop_input, start_row_index); + + typename bls12_exponentiation_type::input_type exponentiation_input = { + miller_loop_result.output[0], + miller_loop_result.output[1], + miller_loop_result.output[2], + miller_loop_result.output[3], + miller_loop_result.output[4], + miller_loop_result.output[5], + miller_loop_result.output[6], + miller_loop_result.output[7], + miller_loop_result.output[8], + miller_loop_result.output[9], + miller_loop_result.output[10], + miller_loop_result.output[11] + }; + typename bls12_exponentiation_type::result_type exp_result = + generate_circuit(exponentiation_instance, bp, assignment, exponentiation_input, + start_row_index + miller_loop_instance.rows_amount); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + typename plonk_bls12_381_pairing::result_type res = { + exp_result.output[0], + exp_result.output[1], + exp_result.output[2], + exp_result.output[3], + exp_result.output[4], + exp_result.output[5], + exp_result.output[6], + exp_result.output[7], + exp_result.output[8], + exp_result.output[9], + exp_result.output[10], + exp_result.output[11] + }; + return res; + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BLS12_381_PAIRING_HPP diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/bls12_exponentiation.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/bls12_exponentiation.hpp new file mode 100644 index 000000000..1a77aa910 --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/bls12_exponentiation.hpp @@ -0,0 +1,498 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of the exponentiation for BLS12-381 pairing +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BLS12_EXPONENTIATION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BLS12_EXPONENTIATION_HPP + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +namespace nil { + namespace blueprint { + namespace components { + // + // Component for raising to power N = (p^12 - 1)/(t^4 - t^2 + 1) + // with -t = 0xD201000000010000 in F_p^12 + // Input: x[12] + // Output: y[12]: y = x^N as elements of F_p^12 + // + // We realize the circuit in two versions - 12-column and 24-column. + // + + using namespace detail; + + template + class bls12_exponentiation; + + template + class bls12_exponentiation> + : public plonk_component { + + static std::size_t gates_amount_internal(std::size_t witness_amount) { + return (witness_amount == 12) ? 8 : 9; + } + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + using power_tm1sq3_type = fp12_power_tm1sq3>; + using power_t_type = fp12_power_t>; + + class gate_manifest_type : public component_gate_manifest { + public: + std::size_t witness_amount; + + gate_manifest_type(std::size_t witness_amount_) : witness_amount(witness_amount_) {} + + std::uint32_t gates_amount() const override { + return bls12_exponentiation::gates_amount_internal(witness_amount); + } + + bool operator<(const component_gate_manifest *other) const override { + return (witness_amount < dynamic_cast(other)->witness_amount); + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t lookup_column_amount) { + gate_manifest manifest = + gate_manifest(gate_manifest_type(witness_amount)) ; +// .merge_with(power_tm1sq3_type::get_gate_manifest(witness_amount,lookup_column_amount)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(12,24,12)), // 12 or 24 + false + ).merge_with(power_t_type::get_manifest()) + .merge_with(power_tm1sq3_type::get_manifest()); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t lookup_column_amount) { + return ((witness_amount == 12)? (8 + 3 + 3 + 4 + 10) : (5 + 2 + 2 + 2 + 6)) + + power_tm1sq3_type::get_rows_amount(witness_amount,lookup_column_amount) + + 3 * power_t_type::get_rows_amount(witness_amount, lookup_column_amount); + } + + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), 0); + + struct input_type { + std::array x; + + std::vector> all_vars() { + return {x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11]}; + } + }; + + struct result_type { + std::array output; + + result_type(const bls12_exponentiation &component, std::uint32_t start_row_index) { + const std::size_t WA = component.witness_amount(); + std::size_t last_row = start_row_index + component.rows_amount - 1; + + for(std::size_t i = 0; i < 12; i++) { + output[i] = var(component.W(i), last_row, false, var::column_type::witness); + } + } + + std::vector> all_vars() { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template + explicit bls12_exponentiation(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + bls12_exponentiation(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + bls12_exponentiation( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_bls12_exponentiation = + bls12_exponentiation>; + + template + typename plonk_bls12_exponentiation::result_type generate_assignments( + const plonk_bls12_exponentiation &component, + assignment> + &assignment, + const typename plonk_bls12_exponentiation::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_bls12_exponentiation; + using var = typename component_type::var; + using value_type = typename BlueprintFieldType::value_type; + + typename BlueprintFieldType::integral_type field_p = BlueprintFieldType::modulus; + const std::size_t WA = component.witness_amount(); + + std::array x; + + for(std::size_t i = 0; i < 12; i++) { + x[i] = var_value(assignment, instance_input.x[i]); + assignment.witness(component.W(i),start_row_index) = x[i]; + } + + using policy_type_fp12 = crypto3::algebra::fields::fp12_2over3over2; + using fp12_element = typename policy_type_fp12::value_type; + + fp12_element F = fp12_element({ {x[0],x[1]}, {x[2],x[3]}, {x[4],x[5]} }, { {x[6],x[7]}, {x[8],x[9]}, {x[10],x[11]} }), + Y = F; + std::size_t slot = 0; + + auto fill_slot = [&assignment, &component, &start_row_index, &slot, &WA](fp12_element V) { + for(std::size_t i = 0; i < 12; i++) { + assignment.witness(component.W((12*slot + i) % WA),start_row_index + (12*slot)/WA) = + V.data[i/6].data[(i % 6)/2].data[i % 2]; + } + slot++; + }; + + auto use_block = [&assignment, &component, &start_row_index, &WA] + (block_type B, std::size_t input_slot, std::size_t &row) { + std::array transfer_vars; + for(std::size_t i = 0; i < 12; i++) { + transfer_vars[i] = var(component.W((12*input_slot + i) % WA),start_row_index + (12*input_slot)/WA,false); + } + typename block_type::input_type block_input = {transfer_vars}; + typename block_type::result_type block_res = generate_assignments(B, assignment, block_input, row); + row += B.rows_amount; + + std::array v; + for(std::size_t i = 0; i < 12; i++) { + v[i] = var_value(assignment, block_res.output[i]); + } + return fp12_element({ {v[0],v[1]}, {v[2],v[3]}, {v[4],v[5]} }, { {v[6],v[7]}, {v[8],v[9]}, {v[10],v[11]} }); + }; + + using power_tm1sq3_type = typename component_type::power_tm1sq3_type; + using power_t_type = typename component_type::power_t_type; + + power_tm1sq3_type power_tm1sq3_instance( component._W, component._C, component._PI); + power_t_type power_t_instance( component._W, component._C, component._PI); + + if (WA == 12) { // F^{p^3} --- in transition to elimination of the conjugation gate + fill_slot(F.inversed()); // F^{-1} + fill_slot(F); // F + fill_slot(F.pow(field_p).pow(field_p).pow(field_p)); + } else { + fill_slot(F); // F + fill_slot(F.inversed()); // F^{-1} + fill_slot(F.inversed().pow(field_p).pow(field_p).pow(field_p)); + fill_slot(F.pow(field_p).pow(field_p).pow(field_p)); + } + Y = F.unitary_inversed(); fill_slot(Y); // F^{p^6} = conjugated(F) = conjugated(a + wb) = a - wb + fill_slot(F.inversed()); // F^{-1} + Y = Y * F.inversed(); fill_slot(Y); // F^{p^6 - 1} + fill_slot(Y.pow(field_p).pow(field_p)); // (F^{p^6 -1})^{p^2} + Y = Y * Y.pow(field_p).pow(field_p); fill_slot(Y); // (F^{p^6 -1})^{p^2 + 1} + + // ---------------- start of g = y^{(1-t)^2/3} + // g, g^{-1} and g^{p^3} + slot--; // rewind to last slot + std::size_t current_row = start_row_index + (12*slot)/WA + 1; + fp12_element G = use_block(power_tm1sq3_instance,slot,current_row); // this computes g = y^{(1-t)^2/3} + slot = ((current_row - start_row_index)*WA)/12; + fill_slot(G.inversed()); fill_slot(G); // G^{-1}, G + fill_slot(G.pow(field_p).pow(field_p).pow(field_p)); // G^{p^3} + + // ---------------- start of g^{-t} + slot--; // rewind to last slot + current_row = start_row_index + (12*slot)/WA + 1; + slot--; // take argument from the one but last slot (i.e. = G) + fp12_element Gmt = use_block(power_t_instance,slot,current_row); // this computes G^{-t} + slot = ((current_row - start_row_index)*WA)/12; + std::size_t gmt_slot = slot; // save for future use + fill_slot(Gmt); // G^{-t} + Gmt = Gmt.inversed(); fill_slot(Gmt); // G^t + if (WA == 24) { fill_slot(Gmt); } // additional slot for alignment when WA = 24 + Gmt = Gmt.pow(field_p).pow(field_p); fill_slot(Gmt); // (G^t)^{p^2} + + // ---------------- start of g^{t^2} + current_row = start_row_index + (12*slot)/WA; // slot is pointing to the start of an empty row now + fp12_element Gt2 = use_block(power_t_instance,gmt_slot,current_row); // this computes G^{t^2} + slot = ((current_row - start_row_index)*WA)/12; + fill_slot(Gt2); fill_slot(G.inversed()); // g^{t^2}, g^{-1} + Gt2 = Gt2 * G.inversed(); fill_slot(Gt2); // g^{t^2-1}, + Gt2 = Gt2.pow(field_p); fill_slot(Gt2); // (g^{t^2-1})^p + + // ---------------- start of g^{-t(t^2-1)} + current_row = start_row_index + (12*slot)/WA; // slot is pointing to the start of an empty row now + fp12_element Gt3 = use_block(power_t_instance,slot-2,current_row); + slot = ((current_row - start_row_index)*WA)/12; + fill_slot(Gt3); fill_slot(Gt3.inversed()); // g^{-t(t^2-1)}, g^{t(t^2-1)} + if (WA == 24) { fill_slot(Gt3.inversed()); } // additional copy for alignment + fill_slot(Y); // get a very old value y, g = y^{(1-t)^2/3} + Y = Y*Gt3.inversed(); fill_slot(Y); // y g^{t(t^2-1)} + fill_slot(G.pow(field_p).pow(field_p).pow(field_p)); // g^{p^3} + Y = Y * G.pow(field_p).pow(field_p).pow(field_p); fill_slot(Y); // y g^{t(t^2-1)} g^{p^3} + fill_slot(Gt2); // (g^{t^2-1})^p + Y = Y * Gt2; fill_slot(Y); // y g^{t(t^2-1)} g^{p^3} (g^{t^2-1})^p + fill_slot(Gmt); // (g^t)^{p^2} + Y = Y * Gmt; fill_slot(Y); // y g^{t(t^2-1)} g^{p^3} (g^{t^2-1})^p (g^t)^{p^2} + + return typename plonk_bls12_exponentiation::result_type( + component, start_row_index); + } + + template + std::vector generate_gates( + const plonk_bls12_exponentiation &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bls12_exponentiation::input_type + &instance_input) { + + using var = typename plonk_bls12_exponentiation::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + using fp12_constraint = detail::abstract_fp12_element; + + const std::size_t WA = component.witness_amount(); + std::vector gate_list = {}; + + fp12_constraint X, Y, Z, C; + + // inversion gate + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W(i), -(WA == 12), true); + Y[i] = var(component.W((i+12) % WA), 0, true); + } + C = X * Y; + + std::vector inversion_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + inversion_constrs.push_back(C[i] - (i > 0? 0 : 1)); + } + gate_list.push_back(bp.add_gate(inversion_constrs)); + + // power p^k gates + std::array,6> Z2; + std::array F; + for( auto &Power : { p_one, p_two, p_three } ) { + for(std::size_t i = 0; i < 12; i++) { + // special fix for p^3 in 24-column var + X[i] = var(component.W((i+12*(Power == p_three)) % WA), -(WA == 12 || Power == p_three), true); + Y[i] = var(component.W((i+12*(Power != p_three)) % WA), 0, true); + } + F = get_fp12_frobenius_coefficients(Power); + for(std::size_t i = 0; i < 6; i++) { + // i -> i/2 + 3(i % 2) is a rearrangement of coefficients by increasing w powers, w.r.t. v = w² + Z2[i][0] = X[2*(i/2 + 3*(i % 2))]; + // Fp2 elements are conjugated when raising to p and p^3 + Z2[i][1] = X[2*(i/2 + 3*(i % 2)) + 1] * (Power != p_two ? -1 : 1); + + C[2*(i/2 + 3*(i % 2))] = Z2[i][0]*F[2*i] - Z2[i][1]*F[2*i+1]; + C[2*(i/2 + 3*(i % 2)) + 1] = Z2[i][0]*F[2*i+1] + Z2[i][1]*F[2*i]; + } + + std::vector frobenius_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + frobenius_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(frobenius_constrs)); + } + // multiplication gate + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W(i), -(WA == 12), true); + Y[i] = var(component.W((i+12) % WA), 0, true); + Z[i] = var(component.W(i), 1, true); + } + C = X * Y; + + std::vector mult_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + mult_constrs.push_back(C[i] - Z[i]); + } + gate_list.push_back(bp.add_gate(mult_constrs)); + + return gate_list; + } + + template + void generate_copy_constraints( + const plonk_bls12_exponentiation &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bls12_exponentiation::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_bls12_exponentiation::var; + + const std::size_t WA = component.witness_amount(); + + std::size_t slot = (WA == 12)? 1 : 0; // location of initial data + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({var(component.W((12*slot + i) % WA), start_row_index + (12*slot)/WA, false), + instance_input.x[i]}); + } + + std::vector> pairs = (WA == 12)? + std::vector>{{0,4}, {112,203}, {7,250}, {114,252}, {205,254}, {159,256}} : + std::vector>{{1,5}, {122,219}, {8,269}, {124,271}, {221,273}, {173,275}, {171,172}, {267,268}}; + for( std::array pair : pairs ) { + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({var(component.W((12*pair[0] + i) % WA), start_row_index + (12*pair[0])/WA, false), + var(component.W((12*pair[1] + i) % WA), start_row_index + (12*pair[1])/WA, false)}); + } + } + } + + template + typename plonk_bls12_exponentiation::result_type generate_circuit( + const plonk_bls12_exponentiation &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bls12_exponentiation::input_type &instance_input, + const std::size_t start_row_index) { + + const std::size_t WA = component.witness_amount(); + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + + auto apply_selector = [&assignment, &selector_index, &start_row_index]( + std::size_t gate_id, std::vector apply_list) { + for( std::size_t row : apply_list ) { + assignment.enable_selector(selector_index[gate_id], start_row_index + row); + } + }; + + // inversion gate #0 + apply_selector(0, (WA == 12)? std::vector{1,113,158,249} : std::vector{0,1,61,85,133}); + + // Frobenius gates (powers p, p^2, p^3) ## 1,2,3 + // p + apply_selector(1, (WA == 12)? std::vector{205} : std::vector{110}); + // p^2 + apply_selector(2, (WA == 12)? std::vector{6,159} : std::vector{3,86}); + // p^3 + apply_selector(3, (WA == 12)? std::vector{2,3,114} : std::vector{1,2,62}); + + // multiplication gate #4 + apply_selector(4, (WA == 12)? std::vector{4,6,203,250,252,254,256} : + std::vector{2,3,109,134,135,136,137}); + + using component_type = plonk_bls12_exponentiation; + using var = typename component_type::var; + using power_tm1sq3_type = typename component_type::power_tm1sq3_type; + using power_t_type = typename component_type::power_t_type; + + power_tm1sq3_type power_tm1sq3_instance( component._W, component._C, component._PI); + power_t_type power_t_instance( component._W, component._C, component._PI); + + std::size_t slot = (WA == 12)? 7 : 8; + std::array transfer_vars; + for(std::size_t i = 0; i < 12; i++) { + transfer_vars[i] = var(component.W((12*slot + i) % WA),start_row_index + (12*slot)/WA,false); + } + typename power_tm1sq3_type::input_type power_tm1sq3_input = {transfer_vars}; + std::size_t current_row = start_row_index + (12*slot)/WA + 1; + typename power_tm1sq3_type::result_type power_tm1sq3_res = + generate_circuit(power_tm1sq3_instance, bp, assignment, power_tm1sq3_input, current_row); + + slot = (WA == 12)? 113 : 123; // slot for linking output + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({power_tm1sq3_res.output[i], + var(component.W((12*slot + i) % WA), start_row_index + (12*slot)/WA, false)}); + } + + std::array block_start, + block_input, + block_output; + if (WA == 12) { + block_start = {115,160,206}; + block_input = {113,157,204}; + block_output= {157,202,248}; + } else { + block_start = {126,174,222}; + block_input = {123,170,220}; + block_output= {170,218,266}; + } + + for(std::size_t j = 0; j < block_start.size(); j++) { + std::array transfer_vars; + std::size_t input_slot = block_input[j]; + for(std::size_t i = 0; i < 12; i++) { + transfer_vars[i] = var(component.W((12*input_slot + i) % WA),start_row_index + (12*input_slot)/WA,false); + } + typename power_t_type::input_type power_t_input = {transfer_vars}; + current_row = start_row_index + (12*block_start[j])/WA; + typename power_t_type::result_type power_t_res = + generate_circuit(power_t_instance, bp, assignment, power_t_input, current_row); + std::size_t output_slot = block_output[j]; + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({power_t_res.output[i], + var(component.W((12*output_slot + i) % WA), start_row_index + (12*output_slot)/WA, false)}); + } + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_bls12_exponentiation::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BLS12_EXPONENTIATION_HPP diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/bn_exponentiation.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/bn_exponentiation.hpp new file mode 100644 index 000000000..b3d754aab --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/bn_exponentiation.hpp @@ -0,0 +1,490 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of the exponentiation for BN elliptic curve pairings +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BN_EXPONENTIATION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BN_EXPONENTIATION_HPP + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +/* +#include +#include +*/ +namespace nil { + namespace blueprint { + namespace components { + // + // Component for raising to power N = (p^12 - 1)/r in F_p^12 + // where p = 36t^4 + 36t^3 + 24t^2 + 6t + 1 + // r = 36t^4 + 36t^3 + 18t^2 + 6t + 1 + // with parameter t + // Input: x[12] + // Output: y[12]: y = x^N as elements of F_p^12 + // + // We use the representation N = (p^6 - 1)(p^2 + 1) (p^4 - p^2 + 1)/r + // and compute (p^4 - p^2 + 1)/r according to https://eprint.iacr.org/2007/390.pdf + // as follows: + // a := x^{6t-5}, b := a^p, b := a*b, + // x^{(p^4-p^2+1)/r} = x^{p^3} * (b * (x^p)^2 * x^{p^2})^{6t^2 + 1} * b * (x^p * x)^9 * a * x^4 + // + using namespace detail; + + template + class bn_exponentiation; + + template + class bn_exponentiation> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + using fixed_power_type = fp12_fixed_power>; + + class gate_manifest_type : public component_gate_manifest { + public: + gate_manifest_type() {} + + std::uint32_t gates_amount() const override { + return bn_exponentiation::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t lookup_column_amount, + unsigned long long T) { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); +// .merge_with(fixed_power_type::get_gate_manifest(witness_amount,lookup_column_amount)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(12)), + false + ).merge_with(fixed_power_type::get_manifest()); + + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t lookup_column_amount, unsigned long long T) { + return 48 + 3 * fixed_power_type::get_rows_amount(witness_amount, lookup_column_amount, T); + } + + unsigned long long T; // the BN parameter + + constexpr static const std::size_t gates_amount = 8; + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), 0, T); + + struct input_type { + std::array x; + + std::vector> all_vars() { + return {x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11]}; + } + }; + + struct result_type { + std::array output; + + result_type(const bn_exponentiation &component, std::uint32_t start_row_index) { + std::size_t last_row = start_row_index + component.rows_amount - 1; + + for(std::size_t i = 0; i < 12; i++) { + output[i] = var(component.W(i), last_row, false, var::column_type::witness); + } + } + + std::vector> all_vars() { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template + explicit bn_exponentiation(ContainerType witness, unsigned long long T_) : + component_type(witness, {}, {}, get_manifest()), T(T_) {}; + + template + bn_exponentiation(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, unsigned long long T_) : + component_type(witness, constant, public_input, get_manifest()), T(T_) {}; + + bn_exponentiation( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, unsigned long long T_) : + component_type(witnesses, constants, public_inputs, get_manifest()), T(T_) {}; + }; + + template + using plonk_bn_exponentiation = + bn_exponentiation>; + + template + typename plonk_bn_exponentiation::result_type generate_assignments( + const plonk_bn_exponentiation &component, + assignment> + &assignment, + const typename plonk_bn_exponentiation::input_type + &instance_input, + const std::uint32_t start_row_index) { + using component_type = plonk_bn_exponentiation; + using var = typename component_type::var; + using value_type = typename BlueprintFieldType::value_type; + using fixed_power_type = typename component_type::fixed_power_type; + + fixed_power_type power_t_instance( component._W, component._C, component._PI, component.T ); + std::size_t R = power_t_instance.rows_amount; + + typename BlueprintFieldType::integral_type field_p = BlueprintFieldType::modulus; + + std::array x; + + for(std::size_t i = 0; i < 12; i++) { + x[i] = var_value(assignment, instance_input.x[i]); + } + + using policy_type_fp12 = crypto3::algebra::fields::fp12_2over3over2; + using fp12_element = typename policy_type_fp12::value_type; + + fp12_element X = fp12_element({ {x[0],x[1]}, {x[2],x[3]}, {x[4],x[5]} }, { {x[6],x[7]}, {x[8],x[9]}, {x[10],x[11]} }), + F, A, B, C, D; + + std::size_t row = 0; + auto fill_row = [&assignment, &component, &start_row_index, &row](fp12_element V) { + for(std::size_t i = 0; i < 12; i++) { + assignment.witness(component.W(i),start_row_index + row) = V.data[i/6].data[(i % 6)/2].data[i % 2]; + } + row++; + }; + auto row_vars = [&component, &start_row_index](std::size_t input_row) { + std::array transfer_vars; + for(std::size_t i = 0; i < 12; i++) { + transfer_vars[i] = var(component.W(i),start_row_index + input_row,false); + } + return transfer_vars; + }; + auto use_power_t = [&assignment, &start_row_index, &row, &power_t_instance](std::array transfer_vars) { + typename fixed_power_type::input_type block_input = {transfer_vars}; + typename fixed_power_type::result_type block_res = + generate_assignments(power_t_instance, assignment, block_input, start_row_index + row); + row += power_t_instance.rows_amount; + return block_res.output; + }; + auto vars_to_fp12 = [&assignment](std::array o) { + std::array v; + for(std::size_t i = 0; i < 12; i++) { + v[i] = var_value(assignment, o[i]); + } + return fp12_element({ {v[0],v[1]}, {v[2],v[3]}, {v[4],v[5]} }, { {v[6],v[7]}, {v[8],v[9]}, {v[10],v[11]} }); + }; + + fill_row(X.inversed()); // 0: x^{-1} + fill_row(X); // 1: x + fill_row(X.pow(field_p).pow(field_p).pow(field_p)); // 2: x^{p^3} + F = X.unitary_inversed(); fill_row(F); // 3: x^{p^6} = conjugated(x) = conjugated(a + wb) = a - wb + fill_row(X.inversed()); // 4: x^{-1} + F = F * X.inversed(); fill_row(F); // 5: x^{p^6 - 1} + fill_row(F.pow(field_p).pow(field_p)); // 6: (x^{p^6 -1})^{p^2} + F = F * F.pow(field_p).pow(field_p); fill_row(F); // 7: f = (x^{p^6 -1})^{p^2 + 1} + fill_row(F.pow(4)); // 8: f^4 + fill_row(F.pow(5)); // 9: f^5 + fill_row(F.pow(5).inversed()); // 10: f^{-5} + A = vars_to_fp12(use_power_t(row_vars(7))); // R rows: f^t computation + fill_row(A); // R+11: f^t + A = A.pow(2); fill_row(A); // R+12: f^{2t} + A = A.pow(3); fill_row(A); // R+13: f^{6t} + fill_row(F.pow(5).inversed()); // R+14: f^{-5} + A = A * F.pow(5).inversed(); fill_row(A); // R+15: a = f^{6t-5} + fill_row(A.pow(field_p)); // R+16: a^p + B = A.pow(field_p + 1); fill_row(B); // R+17: b = a^{p+1} + fill_row(F); // R+18: f + fill_row(F.pow(field_p)); // R+19: f^p + fill_row(F.pow(field_p+1)); // R+20: f^{p+1} + fill_row(F.pow(field_p+1).pow(3)); // R+21: (f^{p+1})^3 + fill_row(F.pow(field_p+1).pow(9)); // R+22: (f^{p+1})^9 + fill_row(F.pow(field_p)); // R+23: f^p + fill_row(F.pow(field_p).pow(2)); // R+24: f^{2p} + fill_row(F); // R+25: f + C = F.pow(field_p).pow(field_p); fill_row(C); // R+26: f^{p^2} + fill_row(F.pow(field_p).pow(2)); // R+27: f^{2p} + C = C * F.pow(field_p).pow(2); fill_row(C); // R+28: f^{2p + p^2} + fill_row(B); // R+29: b + C = B * C; fill_row(C); // R+30: c = b*f^{2p + p^2} + D = vars_to_fp12(use_power_t( + use_power_t(row_vars(R+30)))); // 2R rows: c^{t^2} computation + fill_row(D); // 3R+31: c^{t^2} + D = D.pow(2); fill_row(D); // 3R+32: c^{2t^2} + D = D.pow(3); fill_row(D); // 3R+33: c^{6t^2} + fill_row(C); // 3R+34: c + D = D * C; fill_row(D); // 3R+35: c^{6t^2+1} + fill_row(F); // 3R+36: f + fill_row(F.pow(field_p).pow(field_p).pow(field_p)); // 3R+37: f^{p^3} + fill_row(D); // 3R+38: c^{6t^2+1} + D = D * F.pow(field_p).pow(field_p).pow(field_p); + fill_row(D); // 3R+39: f^{p^3} c^{6t^2+1} + fill_row(B); // 3R+40: b + D = D * B; fill_row(D); // 3R+41: f^{p^3} c^{6t^2+1} b + fill_row(F.pow(field_p+1).pow(9)); // 3R+42: (f^{p+1})^9 + D = D * F.pow(field_p+1).pow(9); fill_row(D); // 3R+43: f^{p^3} c^{6t^2+1} b (f^{p+1})^9 + fill_row(A); // 3R+44: a + D = D * A; fill_row(D); // 3R+45: f^{p^3} c^{6t^2+1} b (f^{p+1})^9 a + fill_row(F.pow(4)); // 3R+46: f^4 + D = D * F.pow(4); fill_row(D); // 3R+47: f^{p^3} c^{6t^2+1} b (f^{p+1})^9 a f^4 + + return typename plonk_bn_exponentiation::result_type( + component, start_row_index); + } + + template + std::vector generate_gates( + const plonk_bn_exponentiation &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bn_exponentiation::input_type + &instance_input) { + + using var = typename plonk_bn_exponentiation::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using policy_type_fp12 = crypto3::algebra::fields::fp12_2over3over2; + + using fp12_constraint = detail::abstract_fp12_element; + + std::vector gate_list = {}; + + fp12_constraint X, Y, Z, C; + + // general setup for all Fp12-unitary gates + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + } + + // inversion gate #0 + C = X * Y; + std::vector inversion_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + inversion_constrs.push_back(C[i] - (i > 0? 0 : 1)); + } + gate_list.push_back(bp.add_gate(inversion_constrs)); + + // power p^k gates #1,2,3 + std::array,6> Z2; + std::array F; + for( auto &Power : { p_one, p_two, p_three } ) { + F = get_fp12_frobenius_coefficients(Power); + for(std::size_t i = 0; i < 6; i++) { + // i -> i/2 + 3(i % 2) is a rearrangement of coefficients by increasing w powers, w.r.t. v = w² + Z2[i][0] = X[2*(i/2 + 3*(i % 2))]; + // Fp2 elements are conjugated when raising to p and p^3 + Z2[i][1] = X[2*(i/2 + 3*(i % 2)) + 1] * (Power != p_two ? -1 : 1); + + C[2*(i/2 + 3*(i % 2))] = Z2[i][0]*F[2*i] - Z2[i][1]*F[2*i+1]; + C[2*(i/2 + 3*(i % 2)) + 1] = Z2[i][0]*F[2*i+1] + Z2[i][1]*F[2*i]; + } + + std::vector frobenius_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + frobenius_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(frobenius_constrs)); + } + + // squaring gate #4 + C = X * X; + + std::vector square_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + square_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(square_constrs)); + + // cubing gate #5 + C = X * X * X; + std::vector cube_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + cube_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(cube_constrs)); + + // power-4 gate #6 + C = (X * X) * (X * X); + std::vector pow4_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + pow4_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(pow4_constrs)); + + // multiplication gate (binary) #7 + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + Z[i] = var(component.W(i), 1, true); + } + C = X * Y; + + std::vector mult_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + mult_constrs.push_back(C[i] - Z[i]); + } + gate_list.push_back(bp.add_gate(mult_constrs)); + + return gate_list; + } + + template + void generate_copy_constraints( + const plonk_bn_exponentiation &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bn_exponentiation::input_type &instance_input, + const std::size_t start_row_index, std::size_t R) { // R = number of rows in external sub-circuit + + using var = typename plonk_bn_exponentiation::var; + + // initial data in row 1 + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({var(component.W(i), start_row_index + 1, false), instance_input.x[i]}); + } + + std::vector> pairs = { {0,4}, {10, R+14}, {7, R+18}, {R+19,R+23}, + {7, R+25}, {R+24,R+27}, {R+17,R+29}, {R+30, 3*R+34}, + {7, 3*R+36}, {3*R+35, 3*R+38}, {R+17,3*R+40}, {R+22, 3*R+42}, + {R+15, 3*R+44}, {8, 3*R+46} }; + for( std::array pair : pairs ) { + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({var(component.W(i), start_row_index + pair[0], false), + var(component.W(i), start_row_index + pair[1], false)}); + } + } + } + + template + typename plonk_bn_exponentiation::result_type generate_circuit( + const plonk_bn_exponentiation &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bn_exponentiation::input_type &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_bn_exponentiation; + using var = typename component_type::var; + using fixed_power_type = typename component_type::fixed_power_type; + + fixed_power_type power_t_instance( component._W, component._C, component._PI, component.T ); + std::size_t R = power_t_instance.rows_amount; + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + + auto apply_selector = [&assignment, &selector_index, &start_row_index]( + std::size_t gate_id, std::vector apply_list) { + for( std::size_t row : apply_list ) { + assignment.enable_selector(selector_index[gate_id], start_row_index + row); + } + }; + auto row_vars = [&component, &start_row_index](std::size_t input_row) { + std::array transfer_vars; + for(std::size_t i = 0; i < 12; i++) { + transfer_vars[i] = var(component.W(i),start_row_index + input_row,false); + } + return transfer_vars; + }; + + // inversion gate #0 + apply_selector(0, {1, 10}); + + // Frobenius gates (powers p, p^2, p^3) ## 1,2,3 + // p + apply_selector(1, {R+16, R+19}); + // p^2 + apply_selector(2, {6, R+26}); + // p^3 + apply_selector(3, {2, 3, 3*R+37}); + + // squaring gate #4 + apply_selector(4, {R+12, R+24, 3*R+32}); + + // cubing gate #5 + apply_selector(5, {R+13, R+21, R+22, 3*R+33}); + + // power-4 gate #6 + apply_selector(6, {8}); + + // multiplication gate #7 + apply_selector(7, {4, 6, 8, R+14, R+16, R+19, R+27, R+29, 3*R+34, 3*R+38, 3*R+40, 3*R+42, 3*R+44, 3*R+46}); + + typename fixed_power_type::input_type power_t_input = {row_vars(7)}; + typename fixed_power_type::result_type power_t_res_1 = + generate_circuit(power_t_instance, bp, assignment, power_t_input, start_row_index + 11); + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({power_t_res_1.output[i], var(component.W(i), start_row_index + R + 11, false)}); + } + + power_t_input = {row_vars(R+30)}; + typename fixed_power_type::result_type power_t_res_2 = + generate_circuit(power_t_instance, bp, assignment, power_t_input, start_row_index + R+31); + power_t_input = {power_t_res_2.output}; + typename fixed_power_type::result_type power_t_res_3 = + generate_circuit(power_t_instance, bp, assignment, power_t_input, start_row_index + 2*R+31); + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({power_t_res_3.output[i], var(component.W(i), start_row_index + 3*R + 31, false)}); + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index, R); + + return typename plonk_bn_exponentiation::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BN_EXPONENTIATION_HPP diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/detail/fp12_power_t.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/detail/fp12_power_t.hpp new file mode 100644 index 000000000..0595cfe79 --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/detail/fp12_power_t.hpp @@ -0,0 +1,432 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for F_p^{12} raising to power -t = 0xD201000000010000. +// This is very BLS12-381 specific. We use towered field extension +// F_p^12 = F_p^6[w]/(w^2 - v), +// F_p^6 = F_p^2[v]/(v^3-(u+1)), +// F_p^2 = F_p[u]/(u^2 - (-1)). +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FP12_POWER_T_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FP12_POWER_T_HPP + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + // + // Component for raising to power -t = 0xD201000000010000 in F_p^12 + // Input: x[12] + // Output: y[12]: y = x^{-t} as elements of F_p^12 + // + // We realize the circuit in two versions - 12-column and 24-column + // The order of exponent computation for the 12-column version is: + // + // 1, 3, 12, 1, 13, 26, 104, 1, 105, 210, 4*210, ..., 4^4*210 = 53760, 1, 53761, 4*53761,..., + // 4^16 *53761, 1, 1+4^16*53761, 4(1+4^16*53761), ..., 4^8(1+4^16*53761) = -t + // + // In the 24-column version we compute two exponents per row, + // writing the value 53760 twice for better alignment of gates. + // + + template + class fp12_power_t; + + template + class fp12_power_t> + : public plonk_component { + + static std::size_t gates_amount_internal(std::size_t witness_amount) { + return (witness_amount == 12) ? 4 : 5; + } + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + + class gate_manifest_type : public component_gate_manifest { + public: + std::size_t witness_amount; + + gate_manifest_type(std::size_t witness_amount_) : witness_amount(witness_amount_) {} + + std::uint32_t gates_amount() const override { + return fp12_power_t::gates_amount_internal(witness_amount); + } + + bool operator<(const component_gate_manifest *other) const override { + return (witness_amount < dynamic_cast(other)->witness_amount); + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t lookup_column_amount) { + static gate_manifest manifest = gate_manifest(gate_manifest_type(witness_amount)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(12,24,12)), // 12 or 24 + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t lookup_column_amount) { + return (witness_amount == 12)? 42 : 22; // 12 -> 42, 24 -> 22 + } + +// constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), 0); + + struct input_type { + std::array x; + + std::vector> all_vars() { + return {x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11]}; + } + }; + + struct result_type { + std::array output; + + result_type(const fp12_power_t &component, std::uint32_t start_row_index) { + const std::size_t WA = component.witness_amount(); + std::size_t last_row = start_row_index + ((WA == 12)? 41 : 21); + + for(std::size_t i = 0; i < 12; i++) { + output[i] = var(component.W(i), last_row, false, var::column_type::witness); + } + } + + std::vector> all_vars() { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template + explicit fp12_power_t(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + fp12_power_t(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + fp12_power_t( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_fp12_power_t = + fp12_power_t>; + + template + typename plonk_fp12_power_t::result_type generate_assignments( + const plonk_fp12_power_t &component, + assignment> + &assignment, + const typename plonk_fp12_power_t::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using value_type = typename BlueprintFieldType::value_type; + + const std::size_t WA = component.witness_amount(); + + std::array x; + + for(std::size_t i = 0; i < 12; i++) { + x[i] = var_value(assignment, instance_input.x[i]); + assignment.witness(component.W(i),start_row_index) = x[i]; + } + + using policy_type_fp12 = crypto3::algebra::fields::fp12_2over3over2; + using fp12_element = typename policy_type_fp12::value_type; + + fp12_element X = fp12_element({ {x[0],x[1]}, {x[2],x[3]}, {x[4],x[5]} }, { {x[6],x[7]}, {x[8],x[9]}, {x[10],x[11]} }), + Y = X; + + std::size_t slot = 0; + + auto fill_slot = [&](fp12_element V) { + for(std::size_t i = 0; i < 12; i++) { + assignment.witness(component.W((12*slot + i) % WA),start_row_index + (12*slot)/WA) = + V.data[i/6].data[(i % 6)/2].data[i % 2]; + } + slot++; + }; + + fill_slot(X); // X + Y = Y.pow(3); fill_slot(Y); // X^3 + Y = Y.pow(4); fill_slot(Y); // X^12 + fill_slot(X); // X + Y *= X; fill_slot(Y); // X^13 + Y = Y.pow(2); fill_slot(Y); // X^26 + Y = Y.pow(4); fill_slot(Y); // X^104 + fill_slot(X); // X + Y *= X; fill_slot(Y); // X^105 + Y = Y.pow(2); fill_slot(Y); // X^210 + for(std::size_t j = 0; j < 4; j++) { + Y = Y.pow(4); fill_slot(Y); // X^{4*210},...,X^{4^4 * 210} + } + if (WA == 24) { fill_slot(Y); } // additional slot for better alignment when WA=24 + fill_slot(X); // X + Y *= X; fill_slot(Y); // X^53761 + for(std::size_t j = 0; j < 16; j++) { + Y = Y.pow(4); fill_slot(Y); // X^{4*53761},...,X^{4^16 * 53761} + } + fill_slot(X); // X + Y *= X; fill_slot(Y); // X^{1 + 4^16 * 53761} + for(std::size_t j = 0; j < 8; j++) { + Y = Y.pow(4); fill_slot(Y); // X^{4(1 + 4^16*53761)},...,X^{4^8(1 + 4^16 * 53761)} + } + + return typename plonk_fp12_power_t::result_type( + component, start_row_index); + } + + template + std::vector generate_gates( + const plonk_fp12_power_t &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_power_t::input_type + &instance_input) { + + using var = typename plonk_fp12_power_t::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + using fp12_constraint = detail::abstract_fp12_element; + + const std::size_t WA = component.witness_amount(); + std::vector gate_list = {}; // 5 gate ids (if WA==12, the last two are the same) + + fp12_constraint X, Y, Z, C; + + // squaring gate + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W(i), -(WA == 12), true); + Y[i] = var(component.W((i+12) % WA), 0, true); + } + C = X * X; + + std::vector square_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + square_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(square_constrs)); + + // cubing gate + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W(i), -(WA == 12), true); + Y[i] = var(component.W((i+12) % WA), 0, true); + } + C = X * X * X; + + std::vector cube_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + cube_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(cube_constrs)); + + // multiplication gate + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W(i), -(WA == 12), true); + Y[i] = var(component.W((i+12) % WA), 0, true); + Z[i] = var(component.W(i), 1, true); + } + C = X * Y; + + std::vector mult_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + mult_constrs.push_back(C[i] - Z[i]); + } + gate_list.push_back(bp.add_gate(mult_constrs)); + + // power-4 gate type 1 (second column = (first column)^4) + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W(i), -(WA == 12), true); + Y[i] = var(component.W((i+12) % WA), 0, true); + } + C = (X * X) * (X * X); + + std::vector pow4_1_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + pow4_1_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(pow4_1_constrs)); + + // power-4 gate type 2 (first column = (second column, prev row)^4) + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W((i+12) % WA), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = (X * X) * (X * X); + + std::vector pow4_2_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + pow4_2_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(pow4_2_constrs)); + + return gate_list; + } + + template + void generate_copy_constraints( + const plonk_fp12_power_t &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_power_t::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_fp12_power_t::var; + + const std::size_t WA = component.witness_amount(); + std::vector apply_list; + + if (WA == 12) { + apply_list = {0,3,7,14,32}; + } else { + apply_list = {0,3,7,15,33}; + } + // copies of initial data + for( std::size_t slot : apply_list ) { + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({var(component.W((12*slot + i) % WA), start_row_index + (12*slot)/WA, false), + instance_input.x[i]}); + } + } + if (WA == 24) { // 13th and 14th slot are equal + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({ var(component.W((12*13 + i) % WA), start_row_index + (12*13)/WA, false), + var(component.W((12*14 + i) % WA), start_row_index + (12*14)/WA, false) }); + } + } + } + + template + typename plonk_fp12_power_t::result_type generate_circuit( + const plonk_fp12_power_t &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_power_t::input_type &instance_input, + const std::size_t start_row_index) { + + const std::size_t WA = component.witness_amount(); + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + + std::vector apply_list; + + if (WA == 12) { + apply_list = {5,9}; + } else { + apply_list = {2,4}; + } + for( std::size_t row : apply_list ) { + assignment.enable_selector(selector_index[0], start_row_index + row); // square gate + } + + assignment.enable_selector(selector_index[1], start_row_index + (WA == 12)); // cube gate + + if (WA == 12) { + apply_list = {3,7,14,32}; + } else { + apply_list = {1,3,7,16}; + } + for( std::size_t row : apply_list ) { + assignment.enable_selector(selector_index[2], start_row_index + row); // multiplication gate + } + + // power4 gate + if (WA == 12) { + assignment.enable_selector(selector_index[3], start_row_index + 2); + assignment.enable_selector(selector_index[3], start_row_index + 6); + for(std::size_t row = 10; row < 14; row++ ) { + assignment.enable_selector(selector_index[3], start_row_index + row); + } + for(std::size_t row = 16; row < 32; row++ ) { + assignment.enable_selector(selector_index[3], start_row_index + row); + } + for(std::size_t row = 34; row < 42; row++ ) { + assignment.enable_selector(selector_index[3], start_row_index + row); + } + } else { + assignment.enable_selector(selector_index[4], start_row_index + 1); + assignment.enable_selector(selector_index[4], start_row_index + 3); + assignment.enable_selector(selector_index[3], start_row_index + 5); + assignment.enable_selector(selector_index[3], start_row_index + 6); + assignment.enable_selector(selector_index[4], start_row_index + 5); + assignment.enable_selector(selector_index[4], start_row_index + 6); + + for(std::size_t row = 8; row < 16; row++ ) { + assignment.enable_selector(selector_index[3], start_row_index + row); + assignment.enable_selector(selector_index[4], start_row_index + row + 1); + } + for(std::size_t row = 17; row < 21; row++ ) { + assignment.enable_selector(selector_index[3], start_row_index + row); + assignment.enable_selector(selector_index[4], start_row_index + row + 1); + } + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_fp12_power_t::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FP12_POWER_T_HPP diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/detail/fp12_power_tminus1sq_over3.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/detail/fp12_power_tminus1sq_over3.hpp new file mode 100644 index 000000000..6adcb8502 --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/detail/fp12_power_tminus1sq_over3.hpp @@ -0,0 +1,527 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for F_p^{12} raising to power (t-1)^2/3 +// with -t = 0xD201000000010000. +// This is very BLS12-381 specific. We use towered field extension +// F_p^12 = F_p^6[w]/(w^2 - v), +// F_p^6 = F_p^2[v]/(v^3-(u+1)), +// F_p^2 = F_p[u]/(u^2 - (-1)). +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FP12_POWER_TMINUS1SQ3_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FP12_POWER_TMINUS1SQ3_HPP + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + // + // Component for raising to power (1-t)^2/3 with -t = 0xD201000000010000 in F_p^12 + // Input: x[12] + // Output: y[12]: y = x^{(1-t)^2/3} as elements of F_p^12 + // + // We realize the circuit in two versions - 12-column and 24-column. + // + // We first compute x^{(1-t)/3} then raise it to power -t using an external + // subcomponent and finally multiply by x^{(1-t)/3} to obtain the result. + // + + template + class fp12_power_tm1sq3; + + template + class fp12_power_tm1sq3> + : public plonk_component { + + static std::size_t gates_amount_internal(std::size_t witness_amount) { + return (witness_amount == 12) ? 5 : 6; + } + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + using power_t_type = fp12_power_t>; + + class gate_manifest_type : public component_gate_manifest { + public: + std::size_t witness_amount; + + gate_manifest_type(std::size_t witness_amount_) : witness_amount(witness_amount_) {} + + std::uint32_t gates_amount() const override { + return fp12_power_tm1sq3::gates_amount_internal(witness_amount); + } + + bool operator<(const component_gate_manifest *other) const override { + return (witness_amount < dynamic_cast(other)->witness_amount); + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t lookup_column_amount) { + static gate_manifest manifest = + gate_manifest(gate_manifest_type(witness_amount)) ; +// .merge_with(power_t_type::get_gate_manifest(witness_amount,lookup_column_amount)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(12,24,12)), // 12 or 24 + false + ).merge_with(power_t_type::get_manifest()); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t lookup_column_amount) { + return ((witness_amount == 12)? (59+3) : (32+2)) + // 12 -> 59+3, 24 -> 32+2 + power_t_type::get_rows_amount(witness_amount, lookup_column_amount); + } + + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), 0); + + struct input_type { + std::array x; + + std::vector> all_vars() { + return {x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11]}; + } + }; + + struct result_type { + std::array output; + + result_type(const fp12_power_tm1sq3 &component, std::uint32_t start_row_index) { + const std::size_t WA = component.witness_amount(); + std::size_t last_row = start_row_index + component.rows_amount - 1; + + for(std::size_t i = 0; i < 12; i++) { + output[i] = var(component.W(i), last_row, false, var::column_type::witness); + } + } + + std::vector> all_vars() { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template + explicit fp12_power_tm1sq3(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + fp12_power_tm1sq3(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + fp12_power_tm1sq3( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_fp12_power_tm1sq3 = + fp12_power_tm1sq3>; + + template + typename plonk_fp12_power_tm1sq3::result_type generate_assignments( + const plonk_fp12_power_tm1sq3 &component, + assignment> + &assignment, + const typename plonk_fp12_power_tm1sq3::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using value_type = typename BlueprintFieldType::value_type; + + const std::size_t WA = component.witness_amount(); + + std::array x; + + for(std::size_t i = 0; i < 12; i++) { + x[i] = var_value(assignment, instance_input.x[i]); + assignment.witness(component.W(i),start_row_index) = x[i]; + } + + using policy_type_fp12 = crypto3::algebra::fields::fp12_2over3over2; + using fp12_element = typename policy_type_fp12::value_type; + + fp12_element X = fp12_element({ {x[0],x[1]}, {x[2],x[3]}, {x[4],x[5]} }, { {x[6],x[7]}, {x[8],x[9]}, {x[10],x[11]} }), + Y = X, + M = X.pow(21845); // 21845 = (4^8 - 1)/3 + std::size_t slot = 0; + + auto fill_slot = [&](fp12_element V) { + for(std::size_t i = 0; i < 12; i++) { + assignment.witness(component.W((12*slot + i) % WA),start_row_index + (12*slot)/WA) = + V.data[i/6].data[(i % 6)/2].data[i % 2]; + } + slot++; + }; + + fill_slot(X.inversed()); // X^{-1} + fill_slot(X); // X + for(std::size_t j = 0; j < 8; j++) { + Y = Y.pow(4); fill_slot(Y); // X^4,....,X^{4^8} + } + if (WA == 24) { fill_slot(Y); } // additional slot for alignment when WA = 24 + fill_slot(X.inversed()); // X^{-1} + Y = Y * X.inversed(); fill_slot(Y); // X^{4^8-1} + if (WA == 24) { slot++; } // ensure alignment when WA = 24 + fill_slot(M); // X^{(4^8-1)/3} + fill_slot(Y); // power m = (4^8-1)/3 is now computed + // ------------------- start of (1-t)/3 proper computation + fill_slot(M); // X^m + fill_slot(M.pow(2)); // X^{2m} + Y = X.pow(16); fill_slot(Y); // X^{4^2} + fill_slot(X); // X + Y = Y*X; fill_slot(Y); //X^{17} + Y = Y.pow(2); fill_slot(Y); // X^{34} + if (WA == 24) { fill_slot(Y); } // additional slot for alignment when WA = 24 + fill_slot(X); // X + Y = Y*X; fill_slot(Y); // X^{35} + for(std::size_t j = 0; j < 12; j++) { + Y = Y.pow(4); fill_slot(Y); // X^{4 * 35},....,X^{4^12 * 35} + } + Y = Y.pow(2); fill_slot(Y); // X^{2 * 4^12 * 35} + if (WA == 24) { fill_slot(Y); } // additional slot for alignment when WA = 24 + fill_slot(M); // X^m + Y = Y*M; fill_slot(Y); // X^{m + 2 * 4^12 * 35} + for(std::size_t j = 0; j < 8; j++) { + Y = Y.pow(4); fill_slot(Y); // X^{4(m + 2 * 4^12 * 35)},...,X^{4^8(m + 2 * 4^12 * 35)} + } + fill_slot(M); // X^m + Y = Y*M; fill_slot(Y); // X^{m + 4^8(m + 2 * 4^12 * 35)} + for(std::size_t j = 0; j < 8; j++) { + Y = Y.pow(4); fill_slot(Y); // X^{4(m + 4^8(m + 2 * 4^12 * 35))},...,X^{4^8(m + 4^8(m + 2 * 4^12 * 35))} + } + fill_slot(M.pow(2)); // X^{2m} + Y = Y*M.pow(2); fill_slot(Y); // X^{2m + 4^8(m + 4^8(m + 2 * 4^12 * 35))} + fill_slot(X); + Y = Y*X; fill_slot(Y); // X^{1 + 2m + 4^8(m + 4^8(m + 2 * 4^12 * 35))} + // now we need to raise this to power (1-t) + + using component_type = plonk_fp12_power_tm1sq3; + using var = typename component_type::var; + using power_t_type = typename component_type::power_t_type; + + power_t_type power_t_instance( component._W, component._C, component._PI); + + slot--; // rewind to last slot + std::array transfer_vars; + for(std::size_t i = 0; i < 12; i++) { + transfer_vars[i] = var(component.W((12*slot + i) % WA),start_row_index + (12*slot)/WA,false); + } + typename power_t_type::input_type power_t_input = {transfer_vars}; + std::size_t current_row = start_row_index + (12*slot)/WA + 1; + typename power_t_type::result_type power_t_res = + generate_assignments(power_t_instance, assignment, power_t_input, current_row); // this computes x^{-t(1-t)/3} + current_row += power_t_instance.rows_amount; + + std::array z; + for(std::size_t i = 0; i < 12; i++) { + z[i] = var_value(assignment, power_t_res.output[i]); + } + fp12_element Z = fp12_element({ {z[0],z[1]}, {z[2],z[3]}, {z[4],z[5]} }, { {z[6],z[7]}, {z[8],z[9]}, {z[10],z[11]} }); + + slot = ((current_row - start_row_index)*WA)/12; + fill_slot(Z); fill_slot(Y); fill_slot(Y*Z); // x^{-t(1-t)/3}, x^{(1-t)/3}, x^{(1-t)*(1-t)/3} + + return typename plonk_fp12_power_tm1sq3::result_type( + component, start_row_index); + } + + template + std::vector generate_gates( + const plonk_fp12_power_tm1sq3 &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_power_tm1sq3::input_type + &instance_input) { + + using var = typename plonk_fp12_power_tm1sq3::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + using fp12_constraint = detail::abstract_fp12_element; + + const std::size_t WA = component.witness_amount(); + std::vector gate_list = {}; // 5 gate ids (if WA==12, the last two are the same) + + fp12_constraint X, Y, Z, C; + + // squaring gate + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W(i), -(WA == 12), true); + Y[i] = var(component.W((i+12) % WA), 0, true); + } + C = X * X; + + std::vector square_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + square_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(square_constrs)); + + // cubing gate + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W(i), -(WA == 12), true); + Y[i] = var(component.W((i+12) % WA), 0, true); + } + C = X * X * X; + + std::vector cube_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + cube_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(cube_constrs)); + + // multiplication gate + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W(i), -(WA == 12), true); + Y[i] = var(component.W((i+12) % WA), 0, true); + Z[i] = var(component.W(i), 1, true); + } + C = X * Y; + + std::vector mult_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + mult_constrs.push_back(C[i] - Z[i]); + } + gate_list.push_back(bp.add_gate(mult_constrs)); + + // inversion gate + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W(i), -(WA == 12), true); + Y[i] = var(component.W((i+12) % WA), 0, true); + } + C = X * Y; + + std::vector inversion_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + inversion_constrs.push_back(C[i] - (i > 0? 0 : 1)); + } + gate_list.push_back(bp.add_gate(inversion_constrs)); + + // power-4 gate type 1 (second column = (first column)^4) + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W(i), -(WA == 12), true); + Y[i] = var(component.W((i+12) % WA), 0, true); + } + C = (X * X) * (X * X); + + std::vector pow4_1_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + pow4_1_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(pow4_1_constrs)); + + // power-4 gate type 2 (first column = (second column, prev row)^4) + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W((i+12) % WA), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = (X * X) * (X * X); + + std::vector pow4_2_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + pow4_2_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(pow4_2_constrs)); + + return gate_list; + } + + template + void generate_copy_constraints( + const plonk_fp12_power_tm1sq3 &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_power_tm1sq3::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_fp12_power_tm1sq3::var; + + const std::size_t WA = component.witness_amount(); + + // copies of initial data + std::vector apply_list = (WA == 12) ? + std::vector{1, 14 + 3, 14 + 6, 14 + 43}: + std::vector{1, 16 + 3, 16 + 7, 16 + 45}; + + for( std::size_t slot : apply_list ) { + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({var(component.W((12*slot + i) % WA), start_row_index + (12*slot)/WA, false), + instance_input.x[i]}); + } + } + + std::vector> pairs = (WA == 12) ? + std::vector> { + {12, 14 + 0}, {12, 14 + 21}, {12, 14 + 31}, // copies of x^m + {11, 13}, // copies of x^{4^8 - 1} + {14 + 1, 14 + 41}, // copies of x^{2m} + {58, 102} // copies of x^{(1-t)/3} + } : + std::vector> { + {14, 16 + 0}, {14, 16 + 23}, {14, 16 + 33}, // copies of x^m + {12, 15}, // copies of x^{4^8 - 1} + {16 + 1, 16 + 43}, // copies of x^{2m} + {62, 109}, // copies of x^{(1-t)/3} + {9, 10}, {16 + 5, 16+5+1}, {16 + 21, 16+21+1} // additional copies for alignment + }; + for( std::array pair : pairs ) { + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({var(component.W((12*pair[0] + i) % WA), start_row_index + (12*pair[0])/WA, false), + var(component.W((12*pair[1] + i) % WA), start_row_index + (12*pair[1])/WA, false)}); + } + } + + } + + template + typename plonk_fp12_power_tm1sq3::result_type generate_circuit( + const plonk_fp12_power_tm1sq3 &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_power_tm1sq3::input_type &instance_input, + const std::size_t start_row_index) { + + const std::size_t WA = component.witness_amount(); + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + std::vector apply_list; + + auto apply_selector = [&assignment, &selector_index, &start_row_index]( + std::size_t gate_id, std::vector apply_list) { + for( std::size_t row : apply_list ) { + assignment.enable_selector(selector_index[gate_id], start_row_index + row); + } + }; + + // square gate #0 + apply_selector(0, (WA == 12)? std::vector{14+1,14+5,14+20}: std::vector{8+0,8+2,8+10}); + + // cube gate #1 + apply_selector(1, (WA == 12)? std::vector{13}: std::vector{7}); + + // multiplication gate #2 + apply_selector(2, (WA == 12)? + std::vector{10, 14 + 3, 14 + 6, 14 + 21, 14 + 31,14+ 41, 14+ 43, 14 + 45 + 42 + 3 - 2 }: + std::vector{5, 8 + 1, 8 + 3, 8 + 11, 8 + 16, 8 + 21, 8 + 22, 8 + 24 + 22 + 2 - 2}); + + // inversion gate #3 + apply_selector(3, (WA == 12)? std::vector{1}: std::vector{0}); + + // power4 gate + if (WA == 12) { + for(std::size_t row = 2; row < 10; row++ ) { + assignment.enable_selector(selector_index[4], start_row_index + row); + } + for(std::size_t row = 14 + 8; row < 14 + 8 + 12; row++ ) { + assignment.enable_selector(selector_index[4], start_row_index + row); + } + for(std::size_t row = 14 + 23; row < 14 + 23 + 8; row++ ) { + assignment.enable_selector(selector_index[4], start_row_index + row); + } + for(std::size_t row = 14 + 33; row < 14 + 33 + 8; row++) { + assignment.enable_selector(selector_index[4], start_row_index + row); + } + } else { + for(std::size_t row = 1; row < 5; row++ ) { + assignment.enable_selector(selector_index[4], start_row_index + row); + assignment.enable_selector(selector_index[5], start_row_index + row); + } + for(std::size_t row = 8 + 4; row < 8 + 10; row++ ) { + assignment.enable_selector(selector_index[4], start_row_index + row); + assignment.enable_selector(selector_index[5], start_row_index + row + 1); + } + for(std::size_t row = 8 + 12; row < 8 + 16; row++ ) { + assignment.enable_selector(selector_index[4], start_row_index + row); + assignment.enable_selector(selector_index[5], start_row_index + row + 1); + } + for(std::size_t row = 8 + 17; row < 8 + 21; row++ ) { + assignment.enable_selector(selector_index[4], start_row_index + row); + assignment.enable_selector(selector_index[5], start_row_index + row + 1); + } + } + + using component_type = plonk_fp12_power_tm1sq3; + using var = typename component_type::var; + using power_t_type = typename component_type::power_t_type; + + power_t_type power_t_instance( component._W, component._C, component._PI); + + std::size_t slot = (WA == 12)? (14+45-1) : (16+47-1); // the number of the final 12-block slot + std::array transfer_vars; + for(std::size_t i = 0; i < 12; i++) { + transfer_vars[i] = var(component.W((12*slot + i) % WA),start_row_index + (12*slot)/WA,false); + } + typename power_t_type::input_type power_t_input = {transfer_vars}; + std::size_t current_row = start_row_index + (12*slot)/WA + 1; + typename power_t_type::result_type power_t_res = + generate_circuit(power_t_instance, bp, assignment, power_t_input, current_row); + + slot = (WA == 12)? 101 : 108; // the block after power (-t) + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({power_t_res.output[i], + var(component.W((12*slot + i) % WA), start_row_index + (12*slot)/WA, false)}); + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_fp12_power_tm1sq3::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FP12_POWER_TMINUS1SQ3_HPP diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/miller_loop.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/miller_loop.hpp new file mode 100644 index 000000000..53663852c --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/miller_loop.hpp @@ -0,0 +1,579 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of unified Miller loop component for BLS12 and BN pairings +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MILLER_LOOP_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MILLER_LOOP_HPP + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + template + std::vector base(T x) { + std::vector res = {(unsigned short int)(x % B)}; + if (x > 0) { + x /= B; + while (x > 0) { + res.insert(res.begin(), x % B); + x /= B; + } + } + return res; + } + } // namespace detail + + // + // Component for computing the result of applying the Miller loop + // to two points P from E(F_p) and Q from E'(F_p^2). + // The loop parameter C_val is passed to the constructor. + // Input: P[2], Q[4] ( we assume P and Q are NOT (0,0), i.e. not the points at infinity, NOT CHECKED ) + // Output: f[12]: an element of F_p^12 + // + // Each iteration of the Miller loop adds two rows to the circuit: + // +------+------+------+------+------+------+-------+-------+-------+-------+ + // | f[0] | f[1] | f[2] | f[3] | f[4] | f[5] | f[6] | f[7] | .... | f[11] | + // +------+------+------+------+------+------+-------+-------+-------+-------+ + // | P[0] | P[1] | T[0] | T[1] | T[2] | T[3] | ZC[0] | ZC[1] | | + // +------+------+------+------+------+------+-------+-------+---------------+ + // + // These two rows are always followed by two similar rows with f := f² * LineFunction(P,T,T) and T:=T+T + // In case the current bit of the loop-driving bit sequence iz 0, these two rows are formed by the + // next iteration of the loop. In case this bit is 1, these two rows are part of the addition block. + // The addition block is designed as follows: + // +------+------+------+------+------+------+-------+-------+------+------+-------+-------+ + // | f[0] | f[1] | f[2] | f[3] | f[4] | f[5] | f[6] | f[7] | f[8] | f[9] | f[10] | f[11] | + // +------+------+------+------+------+------+-------+-------+------+------+-------+-------+ + // | | | T[0] | T[1] | T[2] | T[3] | | + // +------+------+------+------+------+------+---------------------------------------------+ + // | | + // | External subcomponent assuring the computation of T + Q | + // | | + // +------+------+------+------+------+------+-------+-------+------+------+-------+-------+ + // | f[0] | f[1] | f[2] | f[3] | f[4] | f[5] | f[6] | f[7] | f[8] | f[9] | f[10] | f[11] | + // +------+------+------+------+------+------+-------+-------+------+------+-------+-------+ + // | P[0] | P[1] | T[0] | T[1] | T[2] | T[3] | Q[0] | Q[1] | Q[2] | Q[3] | | | + // +------+------+------+------+------+------+-------+-------+------+------+-------+-------+ + // + // The last two rows contain the result of the Miller loop and the result of the last point operation + // (this point data is irrelevant, but kept for the sake of gate uniformity): + // +------+------+------+------+------+------+------+-------+ + // | f[0] | f[1] | f[2] | f[3] | f[4] | f[5] | .... | f[11] | + // +------+------+------+------+------+------+------+-------+ + // | | | T[0] | T[1] | T[2] | T[3] | | + // +------+------+------+------+------+------+--------------+ + // + using namespace detail; + using detail::base; + + template + class miller_loop; + + template + class miller_loop> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + using point_addition_type = bls12_g2_point_addition< + crypto3::zk::snark::plonk_constraint_system>; + + class gate_manifest_type : public component_gate_manifest { + public: + std::size_t witness_amount; + + gate_manifest_type(std::size_t witness_amount_) : witness_amount(witness_amount_) {} + + std::uint32_t gates_amount() const override { + return miller_loop::gates_amount; + } + }; + + static gate_manifest get_gate_manifest(std::size_t witness_amount, + std::size_t lookup_column_amount) { + gate_manifest manifest = + gate_manifest(gate_manifest_type(witness_amount)) + .merge_with(point_addition_type::get_gate_manifest(witness_amount,lookup_column_amount)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(12)), + true // constant column required + ).merge_with(point_addition_type::get_manifest()); + return manifest; + } + + static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t lookup_column_amount, unsigned long long C_val) { + std::vector C_bin = base<2>(C_val); + + return (C_bin.size()-1)*2 + // doubling LineFunctions + (std::count(C_bin.begin(),C_bin.end(),1)-1)* // number of Adding blocks + // LineFunction and point adder + (4 + point_addition_type::get_rows_amount(witness_amount, lookup_column_amount)) + + 2; // final result and extra point (for gate uniformity) + } + + unsigned long long C_val; + std::vector C_bin = base<2>(C_val); + + constexpr static const std::size_t gates_amount = 3; + const std::size_t rows_amount = get_rows_amount(this->witness_amount(), 0, C_val); + + struct input_type { + std::array P; + std::array Q; + + std::vector> all_vars() { + return {P[0], P[1], Q[0], Q[1], Q[2], Q[3]}; + } + }; + + struct result_type { + std::array output; + + result_type(const miller_loop &component, std::uint32_t start_row_index) { + std::size_t res_row = start_row_index + component.rows_amount - 2; + for(std::size_t i = 0; i < 12; i++) { + output[i] = var(component.W(i), res_row, false, var::column_type::witness); + } + } + + std::vector> all_vars() { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template + explicit miller_loop(ContainerType witness, unsigned long long C_val_) : + component_type(witness, {}, {}, get_manifest()), C_val(C_val_) {}; + + template + miller_loop(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, unsigned long long C_val_) : + component_type(witness, constant, public_input, get_manifest()), C_val(C_val_) {}; + + miller_loop( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, unsigned long long C_val_) : + component_type(witnesses, constants, public_inputs, get_manifest()), C_val(C_val_) {}; + }; + + template + using plonk_miller_loop = + miller_loop>; + + template + typename plonk_miller_loop::result_type generate_assignments( + const plonk_miller_loop &component, + assignment> + &assignment, + const typename plonk_miller_loop::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_miller_loop; + using var = typename component_type::var; + using value_type = typename BlueprintFieldType::value_type; + + using policy_type_fp2 = crypto3::algebra::fields::fp2; + using fp2_element = typename policy_type_fp2::value_type; + using curve_point = std::array; + using point_addition_type = typename component_type::point_addition_type; + + std::vector C_bin = component.C_bin; + + point_addition_type point_addition_instance( component._W, component._C, component._PI); + + value_type xP = var_value(assignment, instance_input.P[0]), + yP = var_value(assignment, instance_input.P[1]); + + std::array xQ = {var_value(assignment, instance_input.Q[0]), var_value(assignment, instance_input.Q[1])}, + yQ = {var_value(assignment, instance_input.Q[2]), var_value(assignment, instance_input.Q[3])}; + curve_point Q = { fp2_element(xQ[0], xQ[1]), fp2_element(yQ[0], yQ[1])}; + + auto double_point = [](curve_point P) { + fp2_element lambda = 3*P[0].pow(2) / (2*P[1]), + nu = P[1] - lambda*P[0], + xR = lambda.pow(2) - 2*P[0], + yR = -(lambda*xR + nu); + return curve_point({xR, yR}); + }; + + auto add_point_Q = [&assignment, &component, &start_row_index, &instance_input, &point_addition_instance] + (std::size_t input_row, std::size_t rel_row) { + std::array T, Q; + for(std::size_t i = 0; i < 4; i++) { + T[i] = var(component.W(2 + i),start_row_index + input_row,false); + Q[i] = instance_input.Q[i]; + } + typename point_addition_type::input_type block_input = {T,Q}; + typename point_addition_type::result_type block_res = + generate_assignments(point_addition_instance, assignment, block_input, start_row_index + rel_row); + + std::array R; + for(std::size_t i = 0; i < 4; i++) { + R[i] = var_value(assignment, block_res.R[i]); + } + return curve_point({fp2_element(R[0],R[1]),fp2_element(R[2],R[3])}); + }; + + using policy_type_fp12 = crypto3::algebra::fields::fp12_2over3over2; + using fp12_element = typename policy_type_fp12::value_type; + + auto LineFunctionDouble = [&assignment, &component, &start_row_index, &xP, &yP]( + fp12_element f, curve_point t, std::size_t row) { + fp2_element ty_inv = t[1].inversed(); + std::array T = { t[0].data[0], t[0].data[1], t[1].data[0], t[1].data[1], ty_inv.data[0], ty_inv.data[1] }; + + fp12_element x = fp12_element::one() * xP, + y = fp12_element::one() * yP, + x1 = fp12_element({ {0,0}, {0,0}, {(T[1] + T[0])/2, (T[1] - T[0])/2} }, { {0,0}, {0,0}, {0,0} }), + y1 = fp12_element({ {0,0}, {0,0}, {0,0}}, { {0,0}, {(T[3] + T[2])/2, (T[3] - T[2])/2} , {0,0} }), + g = f.pow(2) * (3*x1.pow(2)*(x-x1)*((2*y1).inversed()) + y1 - y); + + for(std::size_t i = 0; i < 12; i++) { + assignment.witness(component.W(i),start_row_index + row) = f.data[i/6].data[(i % 6)/2].data[i % 2]; + } + assignment.witness(component.W(0),start_row_index + row + 1) = xP; + assignment.witness(component.W(1),start_row_index + row + 1) = yP; + for(std::size_t i = 0; i < 6; i++) { + assignment.witness(component.W(2 + i),start_row_index + row + 1) = T[i]; + } + return g; + }; + + auto LineFunctionAdd = [&assignment, &component, &start_row_index, &xP, &yP, &xQ, &yQ]( + fp12_element f, curve_point t, std::size_t row) { + std::array T = {t[0].data[0], t[0].data[1], t[1].data[0], t[1].data[1]}; + + fp12_element x = fp12_element::one() * xP, + y = fp12_element::one() * yP, + x1 = fp12_element({ {0,0}, {0,0}, {(T[1] + T[0])/2, (T[1] - T[0])/2} }, { {0,0}, {0,0}, {0,0} }), + y1 = fp12_element({ {0,0}, {0,0}, {0,0}}, { {0,0}, {(T[3] + T[2])/2, (T[3] - T[2])/2} , {0,0} }), + x2 = fp12_element({ {0,0}, {0,0}, {(xQ[1] + xQ[0])/2, (xQ[1] - xQ[0])/2} }, { {0,0}, {0,0}, {0,0} }), + y2 = fp12_element({ {0,0}, {0,0}, {0,0}}, { {0,0}, {(yQ[1] + yQ[0])/2, (yQ[1] - yQ[0])/2} , {0,0} }), + l = (y2-y1)*(x2-x1).inversed(), + g = f * (l*(x-x1) + y1 - y); + for(std::size_t i = 0; i < 12; i++) { + assignment.witness(component.W(i),start_row_index + row) = f.data[i/6].data[(i % 6)/2].data[i % 2]; + } + + std::vector second_row = {xP, yP, T[0], T[1], T[2], T[3], xQ[0], xQ[1], yQ[0], yQ[1]}; + for(std::size_t i = 0; i < 10; i++) { + assignment.witness(component.W(i),start_row_index + row + 1) = second_row[i]; + } + return g; + }; + + std::size_t rel_row = 0; // current row relative number + fp12_element f = fp12_element::one(); // initial f value for Miller loop is 1 + curve_point T = Q; + + for(std::size_t i = 1; i < C_bin.size(); i++) { + f = LineFunctionDouble(f,T,rel_row); + T = double_point(T); + rel_row += 2; // 2 rows for each LineFunction + if (C_bin[i]) { + // fill in the output row of the previous doubling LineFunction + for(std::size_t j = 0; j < 12; j++) { + assignment.witness(component.W(j), start_row_index + rel_row) = f.data[j/6].data[(j % 6)/2].data[j % 2]; + } + rel_row++; + // fill in the output doubled point + for(std::size_t j = 0; j < 4; j++) { + assignment.witness(component.W(j+2), start_row_index + rel_row) = T[j/2].data[j % 2]; + } + rel_row++; + // perform point addition + curve_point TplusQ = add_point_Q(rel_row - 1, rel_row); // argument row, output row + rel_row += point_addition_instance.rows_amount; + + f = LineFunctionAdd(f,T,rel_row); + T = TplusQ; + rel_row += 2; // 2 rows for each LineFunction + } + } + // the result + for(std::size_t j = 0; j < 12; j++) { + assignment.witness(component.W(j), start_row_index + rel_row) = f.data[j/6].data[(j % 6)/2].data[j % 2]; + } + // the extra point for gate uniformity + for(std::size_t j = 0; j < 4; j++) { + assignment.witness(component.W(j+2), start_row_index + rel_row + 1) = T[j/2].data[j % 2]; + } + + return typename plonk_miller_loop::result_type( + component, start_row_index); + } + + template + std::vector generate_gates( + const plonk_miller_loop &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_miller_loop::input_type + &instance_input) { + + using var = typename plonk_miller_loop::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + using fp2_constraint = detail::abstract_fp2_element; + + std::vector gate_list = {}; + + constraint_type cnstr_zero = constraint_type(), + cnstr_one = cnstr_zero + 1; + fp2_constraint one = {cnstr_one, cnstr_zero}, + xQ = {var(component.W(2),-1,true), + var(component.W(3),-1,true)}, + yQ = {var(component.W(4),-1,true), + var(component.W(5),-1,true)}, + ZC = {var(component.W(6),-1,true), + var(component.W(7),-1,true)}, + xR = {var(component.W(2),1,true), + var(component.W(3),1,true)}, + yR = {var(component.W(4),1,true), + var(component.W(5),1,true)}, + C; + + // the defining equations are + // xR = (3xQ^2 / 2yP)^2 - 2xQ + // yR = - (3xQ^2 / 2yQ) xR - yQ + (3xQ^2 / 2yQ)xQ + // We transform them into constraints: + // (2yQ)^2 (xR + 2xQ) - (3xQ^2)^2 = 0 + // (2yQ) (yR + yQ) + (3xQ^2)(xR - xQ) = 0 + // Additional constraint to assure that the double of (0,0) is (0,0): + // (ZC * yQ - 1) * yQ = 0 + // (ZC * yQ - 1) * xR = 0 + // (ZC * yQ - 1) * yR = 0 + std::vector doubling_constrs = {}; + + C = (2*yQ)*(2*yQ)*(xR + 2*xQ) - (3*xQ*xQ)*(3*xQ*xQ); + doubling_constrs.push_back(C[0]); + doubling_constrs.push_back(C[1]); + + C = (2*yQ)*(yR + yQ) + (3*xQ*xQ)*(xR - xQ); + doubling_constrs.push_back(C[0]); + doubling_constrs.push_back(C[1]); + + C = (ZC*yQ - one)*yQ; + doubling_constrs.push_back(C[0]); + doubling_constrs.push_back(C[1]); + + C = (ZC*yQ - one)*xR; + doubling_constrs.push_back(C[0]); + doubling_constrs.push_back(C[1]); + + C = (ZC*yQ - one)*yR; + doubling_constrs.push_back(C[0]); + doubling_constrs.push_back(C[1]); + + gate_list.push_back(bp.add_gate(doubling_constrs)); + + // All the following constraints are for Fp12 elements + using fp12_constraint = detail::abstract_fp12_element; + + // LineFunction Doubling case gate + fp12_constraint X, Y, twoX1, twoY1, F, G, C12; + for(std::size_t i = 0; i < 12; i++) { + F[i] = var(component.W(i), -1, true); + G[i] = var(component.W(i), 1, true); + X[i] = constraint_type(); + Y[i] = constraint_type(); + twoX1[i] = constraint_type(); + twoY1[i] = constraint_type(); + } + X[0] = var(component.W(0), 0, true); + Y[0] = var(component.W(1), 0, true); + twoX1[4] = var(component.W(3),0, true) + var(component.W(2),0, true); + twoX1[5] = var(component.W(3),0, true) - var(component.W(2),0, true); + twoY1[8] = var(component.W(5),0, true) + var(component.W(4),0, true); + twoY1[9] = var(component.W(5),0, true) - var(component.W(4),0, true); + + C12 = 8*twoY1*G - F*F*(6*twoX1*twoX1*X - 3*twoX1*twoX1*twoX1 + 4*twoY1*twoY1 - 8*twoY1*Y); + std::vector line_func_double_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + line_func_double_constrs.push_back(C12[i]); + } + gate_list.push_back(bp.add_gate(line_func_double_constrs)); + + // LineFunction Adding case gate + fp12_constraint twoX2, twoY2; + // we REUSE X, Y, twoX1, twoX2, F, G from the previous gate and REDEFINE C12 + for(std::size_t i = 0; i < 12; i++) { + twoX2[i] = constraint_type(); + twoY2[i] = constraint_type(); + } + twoX2[4] = var(component.W(7),0, true) + var(component.W(6),0, true); + twoX2[5] = var(component.W(7),0, true) - var(component.W(6),0, true); + twoY2[8] = var(component.W(9),0, true) + var(component.W(8),0, true); + twoY2[9] = var(component.W(9),0, true) - var(component.W(8),0, true); + + C12 = 2*(twoX2 - twoX1)*G - F*((twoY2 - twoY1)*(2*X - twoX1) - (2*Y - twoY1)*(twoX2 - twoX1)); + std::vector line_func_add_constrs = {}; + for(std::size_t i = 0; i < 12; i++) { + line_func_add_constrs.push_back(C12[i]); + } + gate_list.push_back(bp.add_gate(line_func_add_constrs)); + + return gate_list; + } + + template + void generate_copy_constraints( + const plonk_miller_loop &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_miller_loop::input_type &instance_input, + const std::size_t start_row_index) { + } + + template + typename plonk_miller_loop::result_type generate_circuit( + const plonk_miller_loop &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_miller_loop::input_type &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_miller_loop; + using var = typename component_type::var; + using point_addition_type = typename component_type::point_addition_type; + + point_addition_type point_addition_instance( component._W, component._C, component._PI); + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + + std::vector C_bin = component.C_bin; + + // Copy constraints for the 0-th f row to constants (1,0,...,0) in constant column + bp.add_copy_constraint({var(0, start_row_index + 1, false, var::column_type::constant), + var(component.W(0), start_row_index, false)}); + for(std::size_t j = 1; j < 12; j++) { + bp.add_copy_constraint({var(0, start_row_index, false, var::column_type::constant), + var(component.W(j), start_row_index, false)}); + } + std::size_t rel_row = 0; + for(std::size_t j = 0; j < 4; j++) { + bp.add_copy_constraint({ var(component.W(2+j),start_row_index + 1), instance_input.Q[j]}); + } + + for(std::size_t i = 1; i < C_bin.size(); i++) { + bp.add_copy_constraint({var(component.W(0),start_row_index + rel_row + 1),instance_input.P[0]}); + bp.add_copy_constraint({var(component.W(1),start_row_index + rel_row + 1),instance_input.P[1]}); + assignment.enable_selector(selector_index[1], start_row_index + rel_row + 1); // doubling LineFunction gate + assignment.enable_selector(selector_index[0], start_row_index + rel_row + 2); // point doubling gate + rel_row += 2; // 2 rows for each LineFunction + if (C_bin[i]) { + std::array input_T; + for(std::size_t j = 0; j < 4; j++) { + input_T[j] = var(component.W(2 + j),start_row_index + rel_row + 1,false); + } + // point adder subcomponent + typename point_addition_type::input_type point_addition_input = {input_T, instance_input.Q}; + typename point_addition_type::result_type point_addition_res = + generate_circuit(point_addition_instance, bp, assignment, point_addition_input, start_row_index + rel_row + 2); + // assure a copy of the previous result is passed on to the adding LineFunction + for(std::size_t j = 0; j < 12; j++) { + bp.add_copy_constraint({var(component.W(j),start_row_index + rel_row), + var(component.W(j),start_row_index + rel_row +2+point_addition_instance.rows_amount)}); + } + // assure a copy of the doubled point is passed on to the adding LineFunction + for(std::size_t j = 0; j < 4; j++) { + bp.add_copy_constraint({var(component.W(2+j),start_row_index + rel_row +1), + var(component.W(2+j),start_row_index + rel_row +3+point_addition_instance.rows_amount)}); + } + + // Now that copies are assured we can modify rel_row to be at the top of adding LineFunction + rel_row += 2 + point_addition_instance.rows_amount; + bp.add_copy_constraint({var(component.W(0),start_row_index + rel_row + 1),instance_input.P[0]}); + bp.add_copy_constraint({var(component.W(1),start_row_index + rel_row + 1),instance_input.P[1]}); + for(std::size_t j = 0; j < 4; j++) { + bp.add_copy_constraint({var(component.W(6 + j),start_row_index + rel_row + 1),instance_input.Q[j]}); + } + assignment.enable_selector(selector_index[2], start_row_index + rel_row + 1); // adding LineFunction + // skip 2 more rows after LineFunction + rel_row += 2; + // Link point addition output to cells in circuit + for(std::size_t j = 0; j < 4; j++) { + bp.add_copy_constraint({point_addition_res.R[j], + var(component.W(2 + j), start_row_index + rel_row + 1, false)}); + } + } + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + generate_assignments_constant(component, assignment, instance_input, start_row_index); + + return typename plonk_miller_loop::result_type( + component, start_row_index); + } + template + void generate_assignments_constant( + const plonk_miller_loop &component, + assignment> &assignment, + const typename plonk_miller_loop::input_type &instance_input, + const std::size_t start_row_index) { + + assignment.constant(component.C(0), start_row_index) = 0; // a zero to make copy-constraints with + assignment.constant(component.C(0), start_row_index + 1) = 1; // a one to make copy-constraints with + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MILLER_LOOP_HPP diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp new file mode 100644 index 000000000..12336f795 --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp @@ -0,0 +1,439 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Circuit for final exponentiation for MNT4 elliptic curve pairings +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_EXPONENTIATION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_EXPONENTIATION_HPP + +#include "nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp" +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + // + // Curve E over prime field (F_p) as q points + // + // Pairing is possible due to q | p^k-1 + // + // This component raises element in F_{p^k} to power F = (p^k-1)/q + // + // For MNT4 curve k = 4 and F = (p^2-1)(p+w_0) + // + // The process of raising x to power F takes 6 stages: + // 1. Raise x to power p, x <- x^p (on Fp4 this is cheap) + // 2. Repeat: x <- x^p, now x holds x^(p^2) + // 3. Divide by initial value x, now x holds x^(p^2-1), save to x' + // 4. Raise x' to power p: x1 <- x'^p (cheap) + // 5. Raise x' to power w0: x2 <- x'^w0 (this is hard) + // 6. Result is x1*x2 + // + // Circuit requires 4 witnesses, 4 inputs and 4 outputs + // 6 gates are used: + // Gate 0: Raising to power p, "Frobenius map" + // Gate 1: "Division in Fp4" + // Gate 2: "Multiplication" + // Gate 3: "Squaring" + // Gate 4: "Cubing" + // Gate 5: "Fourth power" + // Gates 3-5 are used for powering to w0 + using namespace detail; + + template + class mnt4_exponentiation; + + template + class mnt4_exponentiation> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + using curve_type = nil::crypto3::algebra::curves::mnt4<298>; + + using fixed_power_type = mnt4_fp4_fixed_power, BlueprintFieldType>; + + class gate_manifest_type : public component_gate_manifest { + public: + gate_manifest_type() {} + + std::uint32_t gates_amount() const override { + return mnt4_exponentiation::gates_amount; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount) + { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(4)), + false + ); + + return manifest; + } + + constexpr static std::size_t get_rows_amount( + std::size_t witness_amount) + { + return fixed_power_type::get_rows_amount(crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0) + 9 - 1; + } + + constexpr static const std::size_t gates_amount = 7; + const std::size_t rows_amount = get_rows_amount(0); + + struct input_type { + std::array x; + + std::vector> all_vars() { + return {x[0], x[1], x[2], x[3]}; + } + }; + + struct result_type { + std::array output; + + result_type(const mnt4_exponentiation &component, std::uint32_t start_row_index) { + std::size_t last_row = start_row_index + component.rows_amount - 1; + for(std::size_t i = 0; i < 4; i++) { + output[i] = var(component.W(i), last_row, false, var::column_type::witness); + } + } + + std::vector> all_vars() { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template + explicit mnt4_exponentiation(ContainerType witness) : + component_type(witness, {}, {}, get_manifest()) {}; + + template + mnt4_exponentiation( + WitnessContainerType witness, + ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + mnt4_exponentiation( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, unsigned long long T_) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_mnt4_exponentiation = + mnt4_exponentiation>; + + template + typename plonk_mnt4_exponentiation::result_type + generate_assignments( + plonk_mnt4_exponentiation const& component, + assignment> &assignment, + typename plonk_mnt4_exponentiation::input_type const& instance_input, + const std::uint32_t start_row_index) + { + using component_type = plonk_mnt4_exponentiation; + using var = typename component_type::var; + using value_type = typename BlueprintFieldType::value_type; + using curve_type = nil::crypto3::algebra::curves::mnt4<298>; + + using fixed_power_type = mnt4_fp4_fixed_power, BlueprintFieldType>; + + fixed_power_type fixed_power_instance(component._W, component._C, component._PI, crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0); + + std::array x; + + for(std::size_t i = 0; i < 4; i++) { + x[i] = var_value(assignment, instance_input.x[i]); + } + + using policy_type_fp4 = crypto3::algebra::fields::fp4; + using fp4_element = typename policy_type_fp4::value_type; + + fp4_element + input = fp4_element({ {x[0],x[1]}, {x[2],x[3]}, }), + elt = input; + + std::size_t row = start_row_index; + + auto fill_row = [&assignment, &component, &row](fp4_element const& V) + { + for(std::size_t i = 0; i < 4; ++i) { + assignment.witness(component.W(i),row) = V.data[i/2].data[i%2]; + } + ++row; + }; + + // Initial value + fill_row(elt); + + // elt <- elt^p + elt = elt.Frobenius_map(1); + fill_row(elt); + + // elt <- (elt^p), now elt holds= x^(p^2) + elt = elt.Frobenius_map(1); + fill_row(elt); + + // elt <- elt/x, elt now holds x^(p^2-1) + fill_row(input); + elt = elt*input.inversed(); + fill_row(elt); + + // elt2 <- elt^p, elt2 = x^(p^2-1)*p + fp4_element elt2 = elt.Frobenius_map(1); + fill_row(elt2); + + /* Fill rows for raising elt = x^(p^2-1) to power w0 */ + + // The input is from 4th row + std::array transfer_vars = { + var(component.W(0), start_row_index + 4, false), + var(component.W(1), start_row_index + 4, false), + var(component.W(2), start_row_index + 4, false), + var(component.W(3), start_row_index + 4, false), + }; + + typename fixed_power_type::input_type pow_input = { transfer_vars }; + typename fixed_power_type::result_type pow_output = + generate_assignments(fixed_power_instance, assignment, pow_input, row); + row += fixed_power_instance.rows_amount; + + fp4_element elt3({ + { + var_value(assignment, pow_output.output[0]), + var_value(assignment, pow_output.output[1]), + } , { + var_value(assignment, pow_output.output[2]), + var_value(assignment, pow_output.output[3]) + } + }); + // Now elt3 holds x^(p^2-1)*w0 + // fill_row(elt3); + + // Final result is elt2*elt3 = x^((p^2-1)*p) * x^((p^2-1)*w0) = x^(p^2-1)*(p+w0) + fill_row(elt2); + elt = elt2*elt3; + fill_row(elt); + + return typename plonk_mnt4_exponentiation::result_type( + component, start_row_index); + } + + template + std::vector + generate_gates( + plonk_mnt4_exponentiation const& component, + circuit> &bp, + assignment> &assignment, + typename plonk_mnt4_exponentiation::input_type const& instance_input) + { + using var = typename plonk_mnt4_exponentiation::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using fp4_constraint = detail::abstract_fp4_element; + + std::vector gate_list = {}; + + std::vector constrs = {}; + + fp4_constraint X, Xn, Xp, R; + + for(std::size_t i = 0; i < 4; ++i) { + X[i] = var(component.W(i), 0, true); + Xn[i] = var(component.W(i), 1, true); + Xp[i] = var(component.W(i), -1, true); + } + + /* Frobenius gate - 0 + * Ensures x_next = x^p = x.frobenius_map(1) + */ + { + using fp4_ep = typename crypto3::algebra::fields::fp4::extension_policy; + using fp2_ep = typename crypto3::algebra::fields::fp2::extension_policy; + + constrs.clear(); + for(std::size_t i = 0; i < 4; ++i) { + typename BlueprintFieldType::value_type + fc4 = fp4_ep::Frobenius_coeffs_c1[i/2], + fc2 = fp2_ep::Frobenius_coeffs_c1[i%2]; + constraint_type coeff = constraint_type() + fc4*fc2; + constrs.push_back(Xn[i] - coeff*X[i]); + } + + gate_list.push_back(bp.add_gate(constrs)); + } + + /* Division gate - 1 + * Ensures x_next = x_prev/x : x_next * x - x_prev = 0 + */ + { + R = Xn*X - Xp; + + constrs.clear(); + constrs.push_back(R[0]); + constrs.push_back(R[1]); + constrs.push_back(R[2]); + constrs.push_back(R[3]); + + gate_list.push_back(bp.add_gate(constrs)); + } + + /* Multiplication gate - 2 + * Ensures x_next = x*x_prev: x_next - x*x_prev = 0 + */ + { + R = Xn - X*Xp; + + constrs.clear(); + constrs.push_back(R[0]); + constrs.push_back(R[1]); + constrs.push_back(R[2]); + constrs.push_back(R[3]); + + gate_list.push_back(bp.add_gate(constrs)); + } + + return gate_list; + } + + template + void generate_copy_constraints( + plonk_mnt4_exponentiation const& component, + circuit> &bp, + assignment> &assignment, + const typename plonk_mnt4_exponentiation::input_type &instance_input, + const std::size_t start_row_index) + { + using component_type = plonk_mnt4_exponentiation; + using var = typename plonk_mnt4_exponentiation::var; + + using fixed_power_type = typename component_type::fixed_power_type; + using curve_type = nil::crypto3::algebra::curves::mnt4<298>; + fixed_power_type power_instance( component._W, component._C, component._PI, + crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0); + std::size_t R = power_instance.rows_amount; + + // initial data in row 0 + for(std::size_t i = 0; i < 4; ++i) { + bp.add_copy_constraint({var(component.W(i), start_row_index, false), instance_input.x[i]}); + } + + // initial data in row 3 + for(std::size_t i = 0; i < 4; ++i) { + bp.add_copy_constraint({var(component.W(i), start_row_index + 3, false), instance_input.x[i]}); + } + + // Copy from 5 row to R+6 row + for(std::size_t i = 0; i < 4; ++i) { + bp.add_copy_constraint({ + var(component.W(i), start_row_index + R + 6, false), + var(component.W(i), start_row_index + 5, false), + }); + } + } + + template + typename plonk_mnt4_exponentiation::result_type + generate_circuit( + plonk_mnt4_exponentiation const& component, + circuit> &bp, + assignment> &assignment, + const typename plonk_mnt4_exponentiation::input_type &instance_input, + const std::size_t start_row_index) + { + using component_type = plonk_mnt4_exponentiation; + using var = typename component_type::var; + using fixed_power_type = typename component_type::fixed_power_type; + using curve_type = nil::crypto3::algebra::curves::mnt4<298>; + + fixed_power_type power_instance( component._W, component._C, component._PI, + crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0); + std::size_t R = power_instance.rows_amount; + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + + // Frobenius gates + assignment.enable_selector(selector_index[0], start_row_index + 0); + assignment.enable_selector(selector_index[0], start_row_index + 1); + assignment.enable_selector(selector_index[0], start_row_index + 4); + + // Division gate + assignment.enable_selector(selector_index[1], start_row_index + 3); + + // Power to w0 sub-circuit takes input from 4th rouw + std::array power_input_vars; + power_input_vars[0] = var(component.W(0), start_row_index + 4, false); + power_input_vars[1] = var(component.W(1), start_row_index + 4, false); + power_input_vars[2] = var(component.W(2), start_row_index + 4, false); + power_input_vars[3] = var(component.W(3), start_row_index + 4, false); + + typename fixed_power_type::input_type power_input = { power_input_vars }; + typename fixed_power_type::result_type power_output = + generate_circuit(power_instance, bp, assignment, power_input, start_row_index + 6); + + // expect result at start_rows_index + 6 + R + + // Multiplication gate + assignment.enable_selector(selector_index[2], start_row_index + R + 6); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_mnt4_exponentiation::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_mnt4_exponentiation_HPP diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.hpp new file mode 100644 index 000000000..0d4627367 --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.hpp @@ -0,0 +1,751 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of Miller loop component for MNT4 pairings +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_MILLER_LOOP_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_MILLER_LOOP_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + template + std::vector base(T x) { + std::vector res = {(std::uint8_t)(x % B)}; + if (x > 0) { + x /= B; + while (x > 0) { + res.insert(res.begin(), std::uint8_t(x % B)); + x /= B; + } + } + return res; + } + } // namespace detail + // + using mnt4_g2_params = crypto3::algebra::curves::detail:: + mnt4_g2_params<298,crypto3::algebra::curves::forms::short_weierstrass>; + + using mnt4_pairing_params = crypto3::algebra::pairing::detail:: + pairing_params; + + // + // Component for computing the result of applying the Miller loop for MNT4 curve + // to two points P from E(F_p) and Q from E'(F_p^2). + // Input: P[2], Q[4] ( we assume P and Q are NOT (0,0), i.e. not the points at infinity, NOT CHECKED ) + // Output: f[4]: an element of F_p^4 + // + // Each iteration of the Miller loop adds "doubling" row to the circuit: + // + // f0 f1 f2 f3 P0 P1 T0 T1 T2 T3 Q0 Q1 Q2 Q3 L0 L1 L2 L3 + // Gate 0: "doubling" + // Constraints: + // 0. UT = untwisted T + // 1. L = (3*UT.x^2+a)/(2*UT.y) + // 2. f_next = f*f*(L*(P.x-UT.x) - (P.y - UT.y)) + // 3. T_next = T + T + // + // If current iteration needs to add addition, then "addition" row is inserted: + // f0 f1 f2 f3 P0 P1 T0 T1 T2 T3 Q0 Q1 Q2 Q3 L0 L1 L2 L3 + // Gate 1: "addition" + // Constraints: + // 0. UT = untwisted T, UQ = untwisted Q + // 1. L = (UT.y - UQ.y) / (UT.x - UQ.x) + // 2. f_next = f*(L*(P.x - UT.x) - (P.y - UT.y)) + // 3. T_next = T + Q + // + // 219 rows total: 147 doubling and 71 addition + 1 row with result + // P is copied in addition and doubling rows of the circuit + // Q is copied only in addition rows. + // Initial value f (1,0,0,0) is copied from constants column + // + // Total number of copy constraints: 724 = 4+2*147+6*71 + // + // We can reduce number of copy constraints by next trick: + // 1. Copy Q in doubling rows too + // 2. To each gate (doubling and addition) add addition 6 constraints: + // w_i = w_i_rot(1), i = 2,4 (P), 9,10,11,12 (Q) + // 3. Leave copy constraints for P and Q on the first row + // Total number of copy constraints will be: + // 4+2+6 = 12 + // At the expense of adding 6 additional constraints to each gate + // + // Witnesses for L0 and L1 could be removed as they are always zero + + using namespace detail; + using detail::base; + + template + class mnt4_miller_loop; + + template + class mnt4_miller_loop> + : public plonk_component { + + public: + using component_type = plonk_component; + using integral_type = typename BlueprintFieldType::integral_type; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::size_t witness_amount; + + gate_manifest_type(std::size_t witness_amount_) : witness_amount(witness_amount_) {} + + std::uint32_t gates_amount() const override { + return mnt4_miller_loop::gates_amount; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount) + { + gate_manifest manifest = + gate_manifest(gate_manifest_type(witness_amount)); + return manifest; + } + + static manifest_type get_manifest() + { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(18)), + true // constant column required + ); + return manifest; + } + + static std::size_t get_rows_amount( + std::size_t witness_amount) + { + std::vector C_bin = base<2>(C_val); + + std::size_t result = 0; + + // doubling blocks x LineFunctions + result += C_bin.size()-1; + // adding blocks x LineFunctions + result += std::count(C_bin.begin(),C_bin.end(), 1) - 1; + // final result (for gate uniformity) + result += 1; + return result; + } + + constexpr static integral_type C_val = mnt4_pairing_params::ate_loop_count; + std::vector C_bin = base<2>(C_val); + + constexpr static const std::size_t gates_amount = 2; + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + + struct input_type { + std::array P; + std::array Q; + + std::vector> all_vars() { + return {P[0], P[1], Q[0], Q[1], Q[2], Q[3]}; + } + }; + + struct result_type { + std::array output; + + result_type(mnt4_miller_loop const& component, std::uint32_t start_row_index) { + std::size_t res_row = start_row_index + component.rows_amount - 1; + for(std::size_t i = 0; i < 4; i++) { + output[i] = var(component.W(i), res_row, false, var::column_type::witness); + } + } + + std::vector> all_vars() { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template + explicit mnt4_miller_loop(ContainerType witness) : + component_type(witness, {}, {}, get_manifest()) {}; + + template + mnt4_miller_loop( + WitnessContainerType witness, + ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + mnt4_miller_loop( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_miller_loop = + mnt4_miller_loop>; + + template + typename plonk_miller_loop::result_type + generate_assignments( + plonk_miller_loop const& component, + assignment> &assignment, + typename plonk_miller_loop::input_type const& instance_input, + const std::uint32_t start_row_index) + { + using component_type = plonk_miller_loop; + using var = typename component_type::var; + using value_type = typename BlueprintFieldType::value_type; + + using policy_type_fp2 = crypto3::algebra::fields::fp2; + using fp2_element = typename policy_type_fp2::value_type; + using curve_point = std::array; + + using curve_type = nil::crypto3::algebra::curves::mnt4<298>; + + value_type + xP = var_value(assignment, instance_input.P[0]), + yP = var_value(assignment, instance_input.P[1]); + + std::array + xQ = {var_value(assignment, instance_input.Q[0]), var_value(assignment, instance_input.Q[1])}, + yQ = {var_value(assignment, instance_input.Q[2]), var_value(assignment, instance_input.Q[3])}; + + curve_point Q = { fp2_element(xQ[0], xQ[1]), fp2_element(yQ[0], yQ[1])}; + + /* Calculate point doubling on E', affine coordinates */ + auto double_point = [](curve_point const& T) { + fp2_element a(curve_type::g2_type<>::params_type::a), + lambda = (3*T[0].pow(2) + a) / (2*T[1]), + xR = lambda.pow(2) - 2*T[0], + yR = (3*T[0])*lambda - lambda.pow(3) - T[1]; + return curve_point({xR, yR}); + }; + + /* Calculate point addition on E', affine coordinates */ + auto add_points = [](curve_point const& T, curve_point const& Q) { + fp2_element + lambda = (T[1] - Q[1])/(T[0] - Q[0]), + xR = lambda*lambda - T[0] - Q[0], + yR = (2*T[0] + Q[0])*lambda - lambda.pow(3) - T[1]; + return curve_point({xR, yR}); + }; + + using policy_type_fp4 = crypto3::algebra::fields::fp4; + using fp4_element = typename policy_type_fp4::value_type; + + auto insert_row_doubling = [ + &double_point, &assignment, &component, &start_row_index, &xP, &yP] + (fp4_element const& f, curve_point& T, std::size_t row) + { + fp4_element x, y, x1, y1, g, three({{3,0},{0,0}}); + + x = fp4_element({ {xP, 0}, {0, 0} }); + y = fp4_element({ {yP, 0}, {0, 0} }); + + // Untwisting: E'/Fp2 -> E/Fp4 + // x * u^-1, y * (uv)^-1 + // MNT4 nr = 0x11, + // u = (0,1), v = ((0,0), (1,0)) + // u^2 = nr, v^2 = u + // u = ((0,1),(0,0)), u^-1 = ((0,1/nr),(0,0)) + // v = ((0,0),(1,0)), (uv)^-1 = ((0,0), (1/nr, 0)) + // + // ((x0,x1),(x2,x3)) * ((0,1/nr),(0,0)) = ( (x1, x0/nr), (x3, x2/nr) ) + // ((x0,x1),(x2,x3)) * ((0,0),(1/nr,0)) = ( (x3, x2/nr), (x0/nr, x1/nr) ) + // + // T is in form (X,Y)/Fp2: + // X: ((x0,x1),(0,0)) * ((0,1/nr),(0,0)) = ( (x1, x0/nr), (0, 0) ) + // Y: ((y0,y1),(0,0)) * ((0,0),(1/nr,0)) = ( (0, 0), (y0/nr, y1/nr) ) + // + value_type nri = policy_type_fp2::extension_policy::non_residue.inversed(); + + x1 = fp4_element({ {T[0].data[1], T[0].data[0]*nri }, {0,0} }); + y1 = fp4_element({ {0,0}, {T[1].data[0]*nri, T[1].data[1]*nri}}); + + fp4_element a({{curve_type::g1_type<>::params_type::a,0},{0,0}}); + + fp4_element lf =(three*x1.pow(2) + a)*(y1+y1).inversed(); + g = f.pow(2) * (lf*(x-x1) + (y1-y)); + + // f + assignment.witness(component.W(0),start_row_index + row) = f.data[0].data[0]; + assignment.witness(component.W(1),start_row_index + row) = f.data[0].data[1]; + assignment.witness(component.W(2),start_row_index + row) = f.data[1].data[0]; + assignment.witness(component.W(3),start_row_index + row) = f.data[1].data[1]; + + // P + assignment.witness(component.W(4),start_row_index + row ) = xP; + assignment.witness(component.W(5),start_row_index + row ) = yP; + + // T <- T+T + assignment.witness(component.W(6),start_row_index + row) = T[0].data[0]; + assignment.witness(component.W(7),start_row_index + row) = T[0].data[1]; + assignment.witness(component.W(8),start_row_index + row) = T[1].data[0]; + assignment.witness(component.W(9),start_row_index + row) = T[1].data[1]; + T = double_point(T); + + // Q is not used in doubling rows + assignment.witness(component.W(10),start_row_index + row) = 0; + assignment.witness(component.W(11),start_row_index + row) = 0; + assignment.witness(component.W(12),start_row_index + row) = 0; + assignment.witness(component.W(13),start_row_index + row) = 0; + + // lf + assignment.witness(component.W(14),start_row_index + row) = lf.data[0].data[0]; + assignment.witness(component.W(15),start_row_index + row) = lf.data[0].data[1]; + assignment.witness(component.W(16),start_row_index + row) = lf.data[1].data[0]; + assignment.witness(component.W(17),start_row_index + row) = lf.data[1].data[1]; + return g; + }; + + auto insert_row_addition = [ + &add_points, &assignment, &component, &start_row_index, &xP, &yP, &Q] + (fp4_element const& f, curve_point& T, std::size_t row) + { + fp4_element x, y, x1, y1, x2, y2, lf, g; + + x = fp4_element({ {xP, 0}, {0, 0} }); + y = fp4_element({ {yP, 0}, {0, 0} }); + + value_type nri = policy_type_fp2::extension_policy::non_residue.inversed(); + + // Untwist T and Q: E'/Fp2 -> E/Fp4 + x1 = fp4_element({ {T[0].data[1], T[0].data[0]*nri }, {0, 0} }); + y1 = fp4_element({ {0, 0}, {T[1].data[0]*nri, T[1].data[1]*nri}}); + + x2 = fp4_element({ {Q[0].data[1], Q[0].data[0]*nri }, {0, 0} }); + y2 = fp4_element({ {0, 0}, {Q[1].data[0]*nri, Q[1].data[1]*nri}}); + + lf = (y2-y1)*(x2-x1).inversed(); + g = f * (lf*(x-x1) + y1 - y); + + // f + assignment.witness(component.W(0),start_row_index + row) = f.data[0].data[0]; + assignment.witness(component.W(1),start_row_index + row) = f.data[0].data[1]; + assignment.witness(component.W(2),start_row_index + row) = f.data[1].data[0]; + assignment.witness(component.W(3),start_row_index + row) = f.data[1].data[1]; + + // P + assignment.witness(component.W(4),start_row_index + row ) = xP; + assignment.witness(component.W(5),start_row_index + row ) = yP; + + // T <- T+Q + assignment.witness(component.W(6),start_row_index + row) = T[0].data[0]; + assignment.witness(component.W(7),start_row_index + row) = T[0].data[1]; + assignment.witness(component.W(8),start_row_index + row) = T[1].data[0]; + assignment.witness(component.W(9),start_row_index + row) = T[1].data[1]; + T = add_points(T, Q); + + // Q + assignment.witness(component.W(10),start_row_index + row) = Q[0].data[0]; + assignment.witness(component.W(11),start_row_index + row) = Q[0].data[1]; + assignment.witness(component.W(12),start_row_index + row) = Q[1].data[0]; + assignment.witness(component.W(13),start_row_index + row) = Q[1].data[1]; + + // lf + assignment.witness(component.W(14),start_row_index + row) = lf.data[0].data[0]; + assignment.witness(component.W(15),start_row_index + row) = lf.data[0].data[1]; + assignment.witness(component.W(16),start_row_index + row) = lf.data[1].data[0]; + assignment.witness(component.W(17),start_row_index + row) = lf.data[1].data[1]; + return g; + }; + + std::size_t rel_row = 0; + + fp4_element f = fp4_element::one(); + curve_point T = Q; + + /* Miller loop */ + for(std::size_t i = 1; i < component.C_bin.size(); ++i) { + f = insert_row_doubling(f, T, rel_row++); + if (component.C_bin[i]) { + f = insert_row_addition(f, T, rel_row++); + } + } + + // The last row contains the result, f. + // f + assignment.witness(component.W(0),start_row_index + rel_row) = f.data[0].data[0]; + assignment.witness(component.W(1),start_row_index + rel_row) = f.data[0].data[1]; + assignment.witness(component.W(2),start_row_index + rel_row) = f.data[1].data[0]; + assignment.witness(component.W(3),start_row_index + rel_row) = f.data[1].data[1]; + + /* + // P + assignment.witness(component.W(4),start_row_index + rel_row ) = xP; + assignment.witness(component.W(5),start_row_index + rel_row ) = yP; + */ + + /* T is needed as previous row has constraints on it */ + assignment.witness(component.W(6),start_row_index + rel_row) = T[0].data[0]; + assignment.witness(component.W(7),start_row_index + rel_row) = T[0].data[1]; + assignment.witness(component.W(8),start_row_index + rel_row) = T[1].data[0]; + assignment.witness(component.W(9),start_row_index + rel_row) = T[1].data[1]; + + /* + // Q + assignment.witness(component.W(10),start_row_index + rel_row) = Q[0].data[0]; + assignment.witness(component.W(11),start_row_index + rel_row) = Q[0].data[1]; + assignment.witness(component.W(12),start_row_index + rel_row) = Q[1].data[0]; + assignment.witness(component.W(13),start_row_index + rel_row) = Q[1].data[1]; + */ + + return typename plonk_miller_loop::result_type( + component, start_row_index); + } + + template + std::vector + generate_gates( + plonk_miller_loop const& component, + circuit> &bp, + assignment> &assignment, + const typename plonk_miller_loop::input_type &instance_input) + { + using var = typename plonk_miller_loop::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using curve_type = nil::crypto3::algebra::curves::mnt4<298>; + using policy_type_fp2 = crypto3::algebra::fields::fp2; + + using fp2_constraint = detail::abstract_fp2_element< + constraint_type, BlueprintFieldType>; + + using fp4_constraint = detail::abstract_fp4_element< + constraint_type, BlueprintFieldType>; + + using value_type = typename BlueprintFieldType::value_type; + + fp2_constraint C; + fp4_constraint C4; + + std::vector gate_list = {}; + constraint_type c_zero = constraint_type(), c_one = c_zero + 1; + constraint_type c_g1_a = c_zero + curve_type::g1_type<>::params_type::a; + constraint_type c_g2_a0 = c_zero + curve_type::g2_type<>::params_type::a.data[0]; + constraint_type c_g2_a1 = c_zero + curve_type::g2_type<>::params_type::a.data[1]; + value_type nri = policy_type_fp2::extension_policy::non_residue.inversed(); + + /* Constraints for the doubling gate + * 1. f = f_prev^2 * line_function_doubling(T,T,P) + * 2. T_next = T + T + */ + std::vector doubling_constrs = {}; + { + fp4_constraint + one4 = {c_one, c_zero, c_zero, c_zero }, + a4 = { c_g1_a, c_zero, c_zero, c_zero, }, + f = { + var(component.W(0), 0, true), + var(component.W(1), 0, true), + var(component.W(2), 0, true), + var(component.W(3), 0, true) + }, + fnext = { + var(component.W(0), 1, true), + var(component.W(1), 1, true), + var(component.W(2), 1, true), + var(component.W(3), 1, true) + }, + x = { + var(component.W(4), 0, true), + c_zero, c_zero, c_zero + }, + y = { + var(component.W(5), 0, true), + c_zero, c_zero, c_zero + }, + x1 = { + var(component.W(7), 0, true), + var(component.W(6), 0, true) * nri, + c_zero, c_zero + }, + y1 = { + c_zero, c_zero, + var(component.W(8), 0, true) * nri, + var(component.W(9), 0, true) * nri + }, + lf = { + var(component.W(14), 0, true), + var(component.W(15), 0, true), + var(component.W(16), 0, true), + var(component.W(17), 0, true) + }; + + C4 = lf*(2*y1) - (3*x1*x1 + a4); + doubling_constrs.push_back(C4[0]); + doubling_constrs.push_back(C4[1]); + doubling_constrs.push_back(C4[2]); + doubling_constrs.push_back(C4[3]); + + C4 = fnext - f*f*(lf*(x - x1) - (y - y1)); + doubling_constrs.push_back(C4[0]); + doubling_constrs.push_back(C4[1]); + doubling_constrs.push_back(C4[2]); + doubling_constrs.push_back(C4[3]); + } + + /* Constraints for point doubling: Tnext = T + T: + * Tnext.x = (3*T.x^2+a)^2/(2T.y)^2 - 2T.x + * Tnext.y = (3*T.x^2+a)/2T.y *(T.x-Tnext.x)-T.y + * Rewrite: + * (Tnext.x + 2*Tx) * (2*T.y)^2 - (3*T.x^2+a)^2 = 0 + * (Tnext.y + T.y) * (2*T.y) - (3*T.x^2+a)*(T.x-Tnext.x) = 0 + */ + fp2_constraint + a = {c_g2_a0, c_g2_a1}, + Tx = {var(component.W(6), 0, true), var(component.W(7), 0, true)}, + Ty = {var(component.W(8), 0, true), var(component.W(9), 0, true)}, + Tnx = {var(component.W(6), 1, true), var(component.W(7), 1, true)}, + Tny = {var(component.W(8), 1, true), var(component.W(9), 1, true)}; + + C = (Tnx + 2*Tx)*(2*Ty)*(2*Ty) - (3*Tx*Tx + a)*(3*Tx*Tx + a); + doubling_constrs.push_back(C[0]); + doubling_constrs.push_back(C[1]); + + C = (Tny + Ty)*(2*Ty) - (3*Tx*Tx + a)*(Tx - Tnx); + doubling_constrs.push_back(C[0]); + doubling_constrs.push_back(C[1]); + + gate_list.push_back(bp.add_gate(doubling_constrs)); + + /* Constraints for the addition row + * 1. f = f_prev * line_function_addition(T,Q,P) + * 2. T = T_prev + Q + */ + std::vector adding_constrs = {}; + { + fp4_constraint + f = { + var(component.W(0), 0, true), + var(component.W(1), 0, true), + var(component.W(2), 0, true), + var(component.W(3), 0, true) + }, + fnext = { + var(component.W(0), 1, true), + var(component.W(1), 1, true), + var(component.W(2), 1, true), + var(component.W(3), 1, true) + }, + x = { + var(component.W(4), 0, true), + c_zero, c_zero, c_zero + }, + y = { + var(component.W(5), 0, true), + c_zero, c_zero, c_zero + }, + x1 = { + var(component.W(7), 0, true), + var(component.W(6), 0, true) * nri, + c_zero, c_zero + }, + y1 = { + c_zero, c_zero, + var(component.W(8), 0, true) * nri, + var(component.W(9), 0, true) * nri + }, + x2 = { + var(component.W(11), 0, true), + var(component.W(10), 0, true) * nri, + c_zero, c_zero + }, + y2 = { + c_zero, c_zero, + var(component.W(12), 0, true) * nri, + var(component.W(13), 0, true) * nri + }, + lf = { + var(component.W(14), 0, true), + var(component.W(15), 0, true), + var(component.W(16), 0, true), + var(component.W(17), 0, true) + }; + + C4 = lf*(x2 - x1) - (y2 - y1); + adding_constrs.push_back(C4[0]); + adding_constrs.push_back(C4[1]); + adding_constrs.push_back(C4[2]); + adding_constrs.push_back(C4[3]); + + C4 = fnext - f*(lf*(x-x1) - (y-y1)); + adding_constrs.push_back(C4[0]); + adding_constrs.push_back(C4[1]); + adding_constrs.push_back(C4[2]); + adding_constrs.push_back(C4[3]); + } + + /* Constraints for point addition: Tnext = T + Q: + * Tnext.x = (Q.y - T.y)^2/(Q.x - T.x)^2- T.x - Q.x + * Tnext.y = (Q.y - T.y)/(Q.x - T.x)*(T.x - Tnext.x) - T.y + * Rewrite: + * (Tnext.x + T.x + Q.x)*(Q.x - T.x)^2 - (Q.y - T.y)^2 = 0 + * (Tnext.y + T.y)*(Q.x - T.x) - (Q.y - T.y) * (T.x - Tnext.x) = 0 + */ + fp2_constraint + Qx = {var(component.W(10), 0, true), var(component.W(11), 0, true)}, + Qy = {var(component.W(12), 0, true), var(component.W(13), 0, true)}; + + C = (Tnx + Tx + Qx)*(Qx - Tx)*(Qx - Tx) - (Qy - Ty)*(Qy - Ty); + adding_constrs.push_back(C[0]); + adding_constrs.push_back(C[1]); + + C = (Tny + Ty)*(Qx - Tx) - (Qy - Ty)*(Tx - Tnx); + adding_constrs.push_back(C[0]); + adding_constrs.push_back(C[1]); + + gate_list.push_back(bp.add_gate(adding_constrs)); + + return gate_list; + } + + template + void generate_copy_constraints( + plonk_miller_loop const& component, + circuit> &bp, + assignment> &assignment, + const typename plonk_miller_loop::input_type &instance_input, + const std::size_t start_row_index) + { + + using component_type = plonk_miller_loop; + using var = typename component_type::var; + + /* Copy constraints for f in the first row to constants (1,0,0,0) in constant column */ + bp.add_copy_constraint({ + var(0, start_row_index + 1, false, var::column_type::constant), + var(component.W(0), start_row_index, false)}); + bp.add_copy_constraint({ + var(0, start_row_index, false, var::column_type::constant), + var(component.W(1), start_row_index, false)}); + bp.add_copy_constraint({ + var(0, start_row_index, false, var::column_type::constant), + var(component.W(2), start_row_index, false)}); + bp.add_copy_constraint({ + var(0, start_row_index, false, var::column_type::constant), + var(component.W(3), start_row_index, false)}); + + /* T on the first row is Q */ + bp.add_copy_constraint({var(component.W(6), start_row_index, false), instance_input.Q[0]}); + bp.add_copy_constraint({var(component.W(7), start_row_index, false), instance_input.Q[1]}); + bp.add_copy_constraint({var(component.W(8), start_row_index, false), instance_input.Q[2]}); + bp.add_copy_constraint({var(component.W(9), start_row_index, false), instance_input.Q[3]}); + + std::size_t row = 0; + + /* Copy P and Q along the circuit */ + for(std::size_t i = 1; i < component.C_bin.size(); ++i) { + // P + bp.add_copy_constraint({var(component.W(4), start_row_index + row, false), instance_input.P[0]}); + bp.add_copy_constraint({var(component.W(5), start_row_index + row, false), instance_input.P[1]}); + ++row; + + if (component.C_bin[i]) { + // P + bp.add_copy_constraint({var(component.W(4), start_row_index + row, false), instance_input.P[0]}); + bp.add_copy_constraint({var(component.W(5), start_row_index + row, false), instance_input.P[1]}); + // Q + bp.add_copy_constraint({var(component.W(10), start_row_index + row, false), instance_input.Q[0]}); + bp.add_copy_constraint({var(component.W(11), start_row_index + row, false), instance_input.Q[1]}); + bp.add_copy_constraint({var(component.W(12), start_row_index + row, false), instance_input.Q[2]}); + bp.add_copy_constraint({var(component.W(13), start_row_index + row, false), instance_input.Q[3]}); + ++row; + } + } + } + + template + typename plonk_miller_loop::result_type + generate_circuit( + plonk_miller_loop const& component, + circuit> &bp, + assignment> &assignment, + const typename plonk_miller_loop::input_type &instance_input, + const std::size_t start_row_index) + { + using component_type = plonk_miller_loop; + using var = typename component_type::var; + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + generate_assignments_constant(component, assignment, instance_input, start_row_index); + + std::size_t row = 0; + for(std::size_t i = 1; i < component.C_bin.size(); i++) { + assignment.enable_selector(selector_index[0], start_row_index + row); + ++row; + if (component.C_bin[i]) { + assignment.enable_selector(selector_index[1], start_row_index + row); + ++row; + } + } + + return typename plonk_miller_loop::result_type( + component, start_row_index); + } + + template + void generate_assignments_constant( + const plonk_miller_loop &component, + assignment> &assignment, + const typename plonk_miller_loop::input_type &instance_input, + const std::size_t start_row_index) { + + // '0' to make copy-constraints with + assignment.constant(component.C(0), start_row_index) = 0; + // '1' to make copy-constraints with + assignment.constant(component.C(0), start_row_index + 1) = 1; + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MILLER_LOOP_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 269133bf9..701f02319 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -77,6 +77,7 @@ set(NON_NATIVE_TESTS_FILES "algebra/fields/plonk/non_native/comparison_flag" "algebra/fields/plonk/non_native/equality_flag" "algebra/fields/plonk/non_native/division_remainder" + "algebra/fields/plonk/non_native/mnt4_fp4_fixed_power" "non_native/plonk/bool_scalar_multiplication" "non_native/plonk/add_mul_zkllvm_compatible" "non_native/plonk/scalar_non_native_range" @@ -87,6 +88,8 @@ set(PLONK_TESTS_FILES "algebra/curves/plonk/unified_addition" "algebra/curves/plonk/variable_base_endo_scalar_mul" "algebra/curves/plonk/endo_scalar" + "algebra/curves/plonk/mnt4_g2" + "algebra/curves/plonk/mnt6_g2" "hashes/plonk/poseidon" "hashes/plonk/sha256" "hashes/plonk/sha512" @@ -185,6 +188,8 @@ set(HASHES_TESTS_FILES "hashes/r1cs/pedersen") set(PAIRING_TESTS_FILES + "algebra/pairing/weierstrass/plonk/mnt4_pairing" + "algebra/pairing/weierstrass/plonk/mnt4_exponentiation" "algebra/pairing/weierstrass/r1cs/miller_loop" "algebra/pairing/weierstrass/r1cs/precomputation") diff --git a/test/algebra/curves/plonk/mnt4_g2.cpp b/test/algebra/curves/plonk/mnt4_g2.cpp new file mode 100644 index 000000000..39ef1c387 --- /dev/null +++ b/test/algebra/curves/plonk/mnt4_g2.cpp @@ -0,0 +1,304 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// +// mnt4-298 g2 group operations tests +// +#define BOOST_TEST_MODULE blueprint_plonk_mnt4_g2_test + +#include + +#include +#include + +#include + +#include + +#include +#include + +// #include +#include + +#include "../../../test_plonk_component.hpp" + +using namespace nil; + +/* +template +void test_mnt4_g2_doubling(std::vector public_input, + typename CurveType::template g2_type<>::value_type expected_res){ + + using curve_type = CurveType; + using BlueprintFieldType = typename curve_type::g2_type<>::field_type::base_field_type; + + constexpr std::size_t WitnessColumns = 10; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::mnt4_g2_point_double; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input)}; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + typename curve_type::g2_type<>::field_type::value_type expected_x = expected_res.X / expected_res.Z.pow(2), + expected_y = expected_res.Y / expected_res.Z.pow(3); + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "G2 doubling test: " << "\n"; + std::cout << "input : " << public_input[0].data << "," << public_input[1].data << "\n"; + std::cout << "input : " << public_input[2].data << "," << public_input[3].data << "\n"; + std::cout << "expected: " << expected_x.data[0] << "," << expected_x.data[1] << ",\n"; + std::cout << " : " << expected_y.data[0] << "," << expected_y.data[1] << ",\n"; + std::cout << "real : " << var_value(assignment, real_res.R[0]).data << "," << var_value(assignment, real_res.R[1]).data << ",\n"; + std::cout << " " << var_value(assignment, real_res.R[2]).data << "," << var_value(assignment, real_res.R[3]).data << "\n\n"; + #endif + assert(expected_x.data[0] == var_value(assignment, real_res.R[0])); + assert(expected_x.data[1] == var_value(assignment, real_res.R[1])); + assert(expected_y.data[0] == var_value(assignment, real_res.R[2])); + assert(expected_y.data[1] == var_value(assignment, real_res.R[3])); + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8, 9},{},{}); + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); +} +*/ + +template +void test_mnt4_g2_adding(std::vector public_input, + typename CurveType::template g2_type<>::value_type expected_res){ + + using curve_type = CurveType; + using BlueprintFieldType = typename curve_type::template g2_type<>::field_type::base_field_type; + + constexpr std::size_t WitnessColumns = 12; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::mnt4_g2_point_addition; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input), + var(0, 4, false, var::column_type::public_input), var(0, 5, false, var::column_type::public_input), + var(0, 6, false, var::column_type::public_input), var(0, 7, false, var::column_type::public_input)}; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + typename curve_type::template g2_type<>::field_type::value_type + expected_x = expected_res.X / expected_res.Z, + expected_y = expected_res.Y / expected_res.Z; + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "G2 addition test: " << "\n"; + std::cout << "input : " << public_input[0].data << "," << public_input[1].data << "\n"; + std::cout << "input : " << public_input[2].data << "," << public_input[3].data << "\n"; + std::cout << "input : " << public_input[4].data << "," << public_input[5].data << "\n"; + std::cout << "input : " << public_input[6].data << "," << public_input[7].data << "\n"; + std::cout << "expected: " << expected_x.data[0] << "," << expected_x.data[1] << ",\n"; + std::cout << " : " << expected_y.data[0] << "," << expected_y.data[1] << ",\n"; + std::cout << "real : " << var_value(assignment, real_res.R[0]).data << "," << var_value(assignment, real_res.R[1]).data << ",\n"; + std::cout << " " << var_value(assignment, real_res.R[2]).data << "," << var_value(assignment, real_res.R[3]).data << "\n\n"; + #endif + assert(expected_x.data[0] == var_value(assignment, real_res.R[0])); + assert(expected_x.data[1] == var_value(assignment, real_res.R[1])); + assert(expected_y.data[0] == var_value(assignment, real_res.R[2])); + assert(expected_y.data[1] == var_value(assignment, real_res.R[3])); + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},{},{}); + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt4_g2_test_298) { + using curve_type = crypto3::algebra::curves::mnt4_298; + using group_type = typename curve_type::g2_type<>; + using base_field_value = curve_type::base_field_type::value_type; + + typedef typename group_type::value_type group_value_type; + typedef typename group_type::field_type::value_type field_value_type; + typedef typename group_type::field_type::integral_type integral_type; + + std::vector test_g2elems = { + group_value_type( + field_value_type( + integral_type("52892640404176485498888258250761939645646664521825416611470707830951434005627159209780591"), + integral_type("6901204673282085722087006015175666329010388388376197780557030626874831981593510811368863")), + field_value_type( + integral_type("222480788507483435783627461803855577456103608392859748425362093958388665097937139549178105"), + integral_type("473457618242655834511111104264708171397269663855594409295889046536564396001862944093226410")), + field_value_type( + integral_type("419596974807779427390393571055019698369335653003329669124056115583450013108627098513858680"), + integral_type("149607627612571838766044058888617698734314670489658399716283333183285408874693707413318397")) + ), + group_value_type( + field_value_type( + integral_type("56255835555724228591723542089270192697164938041379107223328992730173324399928245059358846"), + integral_type("46250255527773554105757214263585520360100725707942955793200058091533464892507217293940531")), + field_value_type( + integral_type("155028384082520109472323091688145499890723086230473105613759084131478639616631592632464625"), + integral_type("465533660221373177386541501858537253360555818961729492963826000064130468052959533407237447")), + field_value_type( + integral_type("23259481593866437183368014567210892368531954759504937115104343246328462130451615306181062"), + integral_type("142713315480817183462228043059993677492078209743390884365670978629380458784025860924246426")) + ), + group_value_type( + field_value_type( + integral_type("8859659475150781061868936856079139502957168242905639519228121136267087266305684178854875"), + integral_type("462209325599907245020439843781343592928438417628218362475292718286375319702359421644405094")), + field_value_type( + integral_type("8410672722910206233875137276404774276152817895657706351449783800341956445001700564449442"), + integral_type("266170852682979521460847880787408143244332632542685084342628203872489818899468691419354183")), + field_value_type( + integral_type("213345896536693606709059646216474009548458629488868196244077001800481238743901641771774253"), + integral_type("387215285727373584540508732471915235262446880063159109792422942735760517635521156886035474") + )), + group_value_type( + field_value_type( + integral_type("357116578643001463088158361423816194240195652226489384244707070187383111136493747697299752"), + integral_type("133321989751366644470214130621947532005434898885127739932165882001667494033947578828408742")), + field_value_type( + integral_type("432662533675187575869083450599090155600620758890519189852405029933257207413356490123814624"), + integral_type("207550629850349306921586414407735983446079495758436164744068036166912411239355446799889327")), + field_value_type( + integral_type("260442821744693454725381166904385451161353726811297898129293278324322544350755150296003001"), + integral_type("448969037883940876285507823354848568485822243947864708103130667212423142784050761290436950")) + ), + }; + + for(std::size_t i = 0; i < test_g2elems.size(); i++) { + std::cout << "Test instance # " << (i+1) << "\n"; + group_value_type P = test_g2elems[i]; + field_value_type px = field_value_type::zero(), + py = field_value_type::zero(); + if (P.Z != field_value_type::zero()) { + px = P.X / P.Z; + py = P.Y / P.Z; + } + + std::cout << "Check point is on curve: "; + std::cout << ((py*py == px*px*px + group_type::params_type::a*px+group_type::params_type::b) ? "YES" : "NO?!"); + std::cout << std::endl; + +/* + // doubling test + std::cout << "Doubling\n"; + test_mnt4_g2_doubling( + std::vector{px.data[0],px.data[1],py.data[0],py.data[1]}, + P*2); + + // test doubling within addition + std::cout << "Doubling by addition\n"; + test_mnt4_g2_adding( + std::vector{ + px.data[0],px.data[1], + py.data[0],py.data[1], + px.data[0],px.data[1], + py.data[0],py.data[1]}, + P*2); + +*/ + // addition tests + field_value_type + qx = field_value_type::zero(), + qy = field_value_type::zero(); + + // test zero addition + std::cout << "Addition P + 0\n"; + test_mnt4_g2_adding( + std::vector{ + px.data[0],px.data[1], + py.data[0],py.data[1], + qx.data[0],qx.data[1], + qy.data[0],qy.data[1]}, + P); + std::cout << "Addition 0 + P\n"; + test_mnt4_g2_adding( + std::vector{ + qx.data[0],qx.data[1], + qy.data[0],qy.data[1], + px.data[0],px.data[1], + py.data[0],py.data[1]}, + P); + + // test opposite addition + std::cout << "Addition of opposites\n"; + group_value_type g2zero = group_value_type(field_value_type::zero(), field_value_type::zero(),field_value_type::zero()); + + test_mnt4_g2_adding( + std::vector{ + px.data[0],px.data[1], + py.data[0],py.data[1], + px.data[0],px.data[1], + -py.data[0],-py.data[1]}, + g2zero); + + // non-trivial addition tests + std::cout << "Non-trivial additions\n"; + for(std::size_t j = i + 1; j < test_g2elems.size(); j++) { + group_value_type Q = test_g2elems[j]; + if (Q.Z != field_value_type::zero()) { + qx = Q.X / Q.Z; + qy = Q.Y / Q.Z; + } + test_mnt4_g2_adding( + std::vector{ + px.data[0],px.data[1], + py.data[0],py.data[1], + qx.data[0],qx.data[1], + qy.data[0],qy.data[1]}, + P + Q); + } + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/algebra/curves/plonk/mnt6_g2.cpp b/test/algebra/curves/plonk/mnt6_g2.cpp new file mode 100644 index 000000000..90c8899ee --- /dev/null +++ b/test/algebra/curves/plonk/mnt6_g2.cpp @@ -0,0 +1,360 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// +// mnt6-298 g2 group operations tests +// +#define BOOST_TEST_MODULE blueprint_plonk_mnt6_g2_test + +#include + +#include +#include + +#include + +#include + +#include +#include + +// #include +#include + +#include "../../../test_plonk_component.hpp" + +using namespace nil; + +/* +template +void test_mnt6_g2_doubling(std::vector public_input, + typename CurveType::template g2_type<>::value_type expected_res){ + + using curve_type = CurveType; + using BlueprintFieldType = typename curve_type::g2_type<>::field_type::base_field_type; + + constexpr std::size_t WitnessColumns = 10; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::mnt6_g2_point_double; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), var(0, 3, false, var::column_type::public_input)}; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + typename curve_type::g2_type<>::field_type::value_type expected_x = expected_res.X / expected_res.Z.pow(2), + expected_y = expected_res.Y / expected_res.Z.pow(3); + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "G2 doubling test: " << "\n"; + std::cout << "input : " << public_input[0].data << "," << public_input[1].data << "\n"; + std::cout << "input : " << public_input[2].data << "," << public_input[3].data << "\n"; + std::cout << "expected: " << expected_x.data[0] << "," << expected_x.data[1] << ",\n"; + std::cout << " : " << expected_y.data[0] << "," << expected_y.data[1] << ",\n"; + std::cout << "real : " << var_value(assignment, real_res.R[0]).data << "," << var_value(assignment, real_res.R[1]).data << ",\n"; + std::cout << " " << var_value(assignment, real_res.R[2]).data << "," << var_value(assignment, real_res.R[3]).data << "\n\n"; + #endif + assert(expected_x.data[0] == var_value(assignment, real_res.R[0])); + assert(expected_x.data[1] == var_value(assignment, real_res.R[1])); + assert(expected_y.data[0] == var_value(assignment, real_res.R[2])); + assert(expected_y.data[1] == var_value(assignment, real_res.R[3])); + }; + + component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8, 9},{},{}); + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); +} +*/ + +template +void test_mnt6_g2_adding(std::vector public_input, + typename CurveType::template g2_type<>::value_type expected_res){ + + using curve_type = CurveType; + using BlueprintFieldType = typename curve_type::template g2_type<>::field_type::base_field_type; + + constexpr std::size_t WitnessColumns = 18; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 1; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::mnt6_g2_point_addition; + + typename component_type::input_type instance_input = { + // Px + var(0, 0, false, var::column_type::public_input), + var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), + // Py + var(0, 3, false, var::column_type::public_input), + var(0, 4, false, var::column_type::public_input), + var(0, 5, false, var::column_type::public_input), + + // Qx + var(0, 6, false, var::column_type::public_input), + var(0, 7, false, var::column_type::public_input), + var(0, 8, false, var::column_type::public_input), + // Qy + var(0, 9, false, var::column_type::public_input), + var(0,10, false, var::column_type::public_input), + var(0,11, false, var::column_type::public_input), + }; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + typename curve_type::template g2_type<>::field_type::value_type + expected_x, expected_y; + if (expected_res.Z.is_zero()) { + expected_x = curve_type::template g2_type<>::field_type::value_type::zero(); + expected_y = curve_type::template g2_type<>::field_type::value_type::zero(); + } else { + typename curve_type::template g2_type<>::field_type::value_type Zinv = expected_res.Z.inversed(); + expected_x = expected_res.X * Zinv; + expected_y = expected_res.Y * Zinv; + } + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "G2 addition test: " << "\n"; + std::cout << "input : " << public_input[0].data << "," << public_input[1].data << "," << public_input[2].data <<"\n"; + std::cout << "input : " << public_input[3].data << "," << public_input[4].data << "," << public_input[5].data <<"\n"; + std::cout << "input : " << public_input[6].data << "," << public_input[7].data << "," << public_input[8].data <<"\n"; + std::cout << "input : " << public_input[9].data << "," << public_input[10].data << "," << public_input[11].data <<"\n"; + std::cout << "expected: " << expected_x.data[0] << "," << expected_x.data[1] << "," << expected_x.data[2]<< ",\n"; + std::cout << " : " << expected_y.data[0] << "," << expected_y.data[1] << "," << expected_y.data[2]<< ",\n"; + std::cout << "real : " << var_value(assignment, real_res.R[0]).data << "," << var_value(assignment, real_res.R[1]).data << var_value(assignment, real_res.R[2]).data<< ",\n"; + std::cout << " " << var_value(assignment, real_res.R[3]).data << "," << var_value(assignment, real_res.R[4]).data << var_value(assignment, real_res.R[5]).data<< "\n\n"; + #endif + assert(expected_x.data[0] == var_value(assignment, real_res.R[0])); + assert(expected_x.data[1] == var_value(assignment, real_res.R[1])); + assert(expected_x.data[2] == var_value(assignment, real_res.R[2])); + + assert(expected_y.data[0] == var_value(assignment, real_res.R[3])); + assert(expected_y.data[1] == var_value(assignment, real_res.R[4])); + assert(expected_y.data[2] == var_value(assignment, real_res.R[5])); + }; + + component_type component_instance( + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17},{},{}); + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input); +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt6_g2_test_298) { + using curve_type = crypto3::algebra::curves::mnt6_298; + using group_type = typename curve_type::g2_type<>; + using base_field_value = curve_type::base_field_type::value_type; + + typedef typename group_type::value_type group_value_type; + typedef typename group_type::field_type::value_type field_value_type; + typedef typename group_type::field_type::integral_type integral_type; + + std::vector test_g2elems = { + group_value_type( + field_value_type( + integral_type("410572335872342339183325877954389281167896050997501997761983589949822080842623278459992567"), + integral_type("314307044839026067525223018303945180155081139371742636247524238720048560361680084544505170"), + integral_type("158659234452931292842435948978492382626898610546253185753858127311130504369266325753339098") + ), + field_value_type( + integral_type("55290080881676751307864570473613840997216757675641387614966502918747507072592902034908090"), + integral_type("347830999995217215128282979068295153047231756085639872681098785072925975582113110773911563"), + integral_type("199179814495291212665505314698640585913795083742799894126556138811544070328576254219887440") + ), + field_value_type( + integral_type("152147989944892098158850409339577235187259297733850345311157831638264117263672697802479761"), + integral_type("167912725432266840222367839073339095644398598025520558591374806661605859446761541245974785"), + integral_type("331031243576643761068537535911377623208851026645510352997272069074654366134134541980930867") + ) + ), + + group_value_type( + field_value_type( + integral_type("10166658553365457766357154516015859670347841891146254814071698064730970884167981999303335"), + integral_type("184036201088257451076418876290706961266476099522064211783595790896093442398111008008727476"), + integral_type("239920136757444558394793712350013800949993423190866863420735416109173285647062601464625022") + ), + field_value_type( + integral_type("437195618919292832114149960160316798331561391437403997319639789718552003915991076011449961"), + integral_type("405237878768087689353987556581304767020339246151926336930952390047157946142114609604716956"), + integral_type("24630580140476246347877127785327612418400618625102992717173132723704786635407289217695364") + ), + field_value_type( + integral_type("290637279541444318176570394537735146075957300777098731742107944342905332791677910040603243"), + integral_type("220666292619033099170672462364316997755501680358596306171670226454923676203721324911311707"), + integral_type("405596381708367489543443222010875769612680750272864781520936742457852790621398191578847943") + ) + ), + group_value_type( + field_value_type( + integral_type("182441104186515953983760056199584327762605522786179659062480606208729364278827988301268744"), + integral_type("376865162590322126059720895812756631240109632841801804841844908312882819679045167533016358"), + integral_type("456472818106312238555816154453048573806107780122719917229453139073306174980669162597077638") + ), + field_value_type( + integral_type("113896697600415421288998661236816945422695185428796548386818467135381824670803097995246347"), + integral_type("182194016854781781982925714589680029821444403511732841339905069780806447008566051225763233"), + integral_type("86112762633946386051938852979721052769728281285469457536961129551084737738582516109544899") + ), + field_value_type( + integral_type("1735638913462906274704655524341775928569487236407649060117974057836026277333248010096645"), + integral_type("41440624793930621151616242575638374086435759772517310050893128595844429919084863546768103"), + integral_type("405117517936571072810931017577432667341170213174948711626363889119648729924632005294320252") + ) + ), + + group_value_type( + field_value_type( + integral_type("404759253671148360708275548849383375193799466379283565909941983114265851843005774004710384"), + integral_type("246751205231337485634874187064532053433806794006236643525394257896802510789201265201243671"), + integral_type("170903321830781128765754981571169196324188389441820586086351045980679949212237255210846654") + ), + field_value_type( + integral_type("340068245122538744438954898100310658079073682919629299229133073915965205415244372504008961"), + integral_type("124192340770029737655645303592363412233665934394965716704604771240430718931324132136688343"), + integral_type("257431893777383482076351320554939081698399843779993187156438717341854629832714657902198362") + ), + field_value_type( + integral_type("438244562103090666761726495447154090529429475967087402019089526426531184561986998219862585"), + integral_type("359265812490602371305030660503521760388514550641469732384013039728248023302712073336412891"), + integral_type("359495980064766520549958068037183867942535776243929570259608210317967877786197862137353102") + ) + ) + + }; + + for(std::size_t i = 0; i < test_g2elems.size(); i++) { + std::cout << "Test instance # " << (i+1) << "\n"; + group_value_type P = test_g2elems[i]; + field_value_type px = field_value_type::zero(), + py = field_value_type::zero(); + if (P.Z != field_value_type::zero()) { + field_value_type Zinv = P.Z.inversed(); + px = P.X * Zinv; + py = P.Y * Zinv; + } + + std::cout << "Check point is on curve: "; + std::cout << ((py*py == px*px*px + group_type::params_type::a*px+group_type::params_type::b) ? "YES" : "NO?!"); + std::cout << std::endl; + +/* + // doubling test + std::cout << "Doubling\n"; + test_mnt6_g2_doubling( + std::vector{px.data[0],px.data[1],py.data[0],py.data[1]}, + P*2); +*/ + + // test doubling within addition + std::cout << "Doubling: P+P\n"; + test_mnt6_g2_adding( + std::vector{ + px.data[0],px.data[1],px.data[2], + py.data[0],py.data[1],py.data[2], + px.data[0],px.data[1],px.data[2], + py.data[0],py.data[1],py.data[2]}, + P+P); + + + // addition tests + field_value_type + qx = field_value_type::zero(), + qy = field_value_type::zero(); + + // test zero addition + std::cout << "Addition P + 0\n"; + test_mnt6_g2_adding( + std::vector{ + px.data[0], px.data[1], px.data[2], + py.data[0], py.data[1], py.data[2], + qx.data[0], qx.data[1], qx.data[2], + qy.data[0], qy.data[1], qy.data[2]}, + P); + std::cout << "Addition 0 + P\n"; + test_mnt6_g2_adding( + std::vector{ + qx.data[0],qx.data[1], qx.data[2], + qy.data[0],qy.data[1], qy.data[2], + px.data[0],px.data[1], px.data[2], + py.data[0],py.data[1], py.data[2]}, + P); + + // test opposite addition + std::cout << "Addition of opposites\n"; + group_value_type g2zero = group_value_type(field_value_type::zero(), field_value_type::zero(),field_value_type::zero()); + + test_mnt6_g2_adding( + std::vector{ + px.data[0], px.data[1], px.data[2], + py.data[0], py.data[1], py.data[2], + px.data[0], px.data[1], px.data[2], + -py.data[0], -py.data[1], -py.data[2]}, + g2zero); + + // non-trivial addition tests + std::cout << "Non-trivial additions\n"; + for(std::size_t j = i + 1; j < test_g2elems.size(); j++) { + std::cout << "P["<( + std::vector{ + px.data[0],px.data[1], px.data[2], + py.data[0],py.data[1], py.data[2], + qx.data[0],qx.data[1], qx.data[2], + qy.data[0],qy.data[1], qy.data[2]}, + P + Q); + } + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/algebra/fields/plonk/non_native/fp2_arithmetic.cpp b/test/algebra/fields/plonk/non_native/fp2_arithmetic.cpp new file mode 100644 index 000000000..ddc8a9580 --- /dev/null +++ b/test/algebra/fields/plonk/non_native/fp2_arithmetic.cpp @@ -0,0 +1,149 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_fields_non_native_fp2_test + +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; +using namespace blueprint::components::detail; + +template +void test_fp2_mul(std::vector public_input) { + constexpr std::size_t WitnessColumns = 12; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 4; + + using ArithmetizationParams = + crypto3::zk::snark::plonk_arithmetization_params; + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment; + + using value_type = typename FieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::fp12_fixed_power; + + typename component_type::input_type instance_input; + typename std::array a; + typename std::array b; + typename std::array expected_res; + + instance_input.x[0] = var(0, 0, false, var::column_type::public_input); + a[0] = public_input[0]; + instance_input.x[1] = var(0, 1, false, var::column_type::public_input); + a[1] = public_input[1]; + + instance_input.x[2] = var(0, 2, false, var::column_type::public_input); + b[0] = public_input[2]; + instance_input.x[3] = var(0, 3, false, var::column_type::public_input); + b[1] = public_input[3]; + + using policy_type_fp2 = crypto3::algebra::fields::fp2; + using fp2_element = typename policy_type_fp2::value_type; + + fp2_element A = fp2_element({ {a[0], a[1], } }); + fp2_element B = fp2_element({ {b[0], b[1], } }); + + fp2_element C = A * B; + + expected_res = { C.data[0], C.data[1] }; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { +#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "Fp2 mul res vs output\n"; + std::cout << std::dec << expected_res[0].data << " =? " << var_value(assignment, real_res.output[0]).data << std::endl; + std::cout << std::dec << expected_res[1].data << " =? " << var_value(assignment, real_res.output[1]).data << std::endl; +#endif + assert(expected_res[0] == var_value(assignment, real_res.output[0])); + assert(expected_res[1] == var_value(assignment, real_res.output[1])); + }; + + component_type component_instance( + {0,1,2,3}, // witnesses + {}, // constants + {}, // public inputs + Power); + + nil::crypto3::test_component ( + component_instance, public_input, result_check, instance_input, nil::crypto3::detail::connectedness_check_type::STRONG, Power); +} + +static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_fields_non_native_fp12_test) { + using field_type = typename crypto3::algebra::fields::bls12_fq<381>; + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + for(std::size_t i = 0; i < random_tests_amount; i++) { + std::cout << "Random test # " << (i+1) << "\n"; + std::vector x = {}; + + for(std::size_t j = 0; j < 12; j++) { + x.push_back(generate_random()); + } + unsigned long long Power = 0xD201000000010000; + test_fp12_fixed_power(x,Power); + + Power = 0x460055555555aaab; + test_fp12_fixed_power(x,Power); + + Power = 4965661367192848881; + test_fp12_fixed_power(x,Power); + } + +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.cpp b/test/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.cpp new file mode 100644 index 000000000..536620f2b --- /dev/null +++ b/test/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.cpp @@ -0,0 +1,160 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// +// exponentiation to a fixed power in Fp4 for MNT4 GT +// +#define BOOST_TEST_MODULE blueprint_plonk_mnt4_fp4_fixed_power_test + +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_mnt4_fp4_fixed_power( + std::vector public_input, + typename CurveType::base_field_type::extended_integral_type power, + typename CurveType::gt_type::value_type expected_res) +{ + using curve_type = CurveType; + using BlueprintFieldType = typename curve_type::base_field_type; + + constexpr std::size_t WitnessColumns = WitnessAmount; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 5; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::mnt4_fp4_fixed_power; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), + var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), + var(0, 3, false, var::column_type::public_input) + }; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "GT (MNT4_FP4) fixed power test: " << "\n"; + std::cout << "input : " << public_input[0].data << "," << public_input[1].data << "\n"; + std::cout << "input : " << public_input[2].data << "," << public_input[3].data << "\n"; + std::cout << "expected: " << expected_res.data[0].data[0] << "," << expected_res.data[0].data[1] << ",\n"; + std::cout << " : " << expected_res.data[1].data[0] << "," << expected_res.data[1].data[1] << ",\n"; + std::cout << "real : " << var_value(assignment, real_res.output[0]).data << "," << var_value(assignment, real_res.output[1]).data << ",\n"; + std::cout << " " << var_value(assignment, real_res.output[2]).data << "," << var_value(assignment, real_res.output[3]).data << "\n\n"; + #endif + assert(expected_res.data[0].data[0] == var_value(assignment, real_res.output[0])); + assert(expected_res.data[0].data[1] == var_value(assignment, real_res.output[1])); + assert(expected_res.data[1].data[0] == var_value(assignment, real_res.output[2])); + assert(expected_res.data[1].data[1] == var_value(assignment, real_res.output[3])); + return true; + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance(witnesses, + std::array{}, + std::array{}, + power); + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, + power); +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt4_fp4_fixed_power_test) { + using curve_type = crypto3::algebra::curves::mnt4_298; + using group_type = typename curve_type::gt_type; + using base_field_value = curve_type::base_field_type::value_type; + + typedef typename group_type::value_type group_value_type; + typedef typename group_type::underlying_field_type::value_type underlying_field_type; + typedef typename group_type::base_field_type::value_type field_value_type; + typedef typename group_type::base_field_type::integral_type integral_type; + + std::vector test_gt_elems = { + group_value_type( + underlying_field_type( +// 4,3 + 0x22c26a3c19d56fc8790485554be5dc4351961a5162c3634965dc8ae56701157e_cppui254, + 0x1e3305b98bf381650491b7b63559d20d662b70f1616e680a19170715b59a3426_cppui254 + ), + underlying_field_type( +// 2,1 + 0x148a1f438a4cd0d807549cb7f9ec9f41dba3d8b14a6b0f2489d9b9f626d6fd31_cppui254, + 0x3cc907ef65b0eff91d027e4771e9116a0b125325627b6bdf55037702220b1b2_cppui254 + ) + ), + }; + + //auto fixed_power = pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0; + auto fixed_power = pairing::detail::pairing_params::final_exponent; +// auto fixed_power = integral_type("100"); + + for(std::size_t i = 0; i < test_gt_elems.size(); i++) { + std::cout << "Test instance # " << (i+1) << "\n"; + group_value_type P = test_gt_elems[i]; + group_value_type R = P.pow(fixed_power); + + test_mnt4_fp4_fixed_power( + std::vector{ P.data[0].data[0], P.data[0].data[1], P.data[1].data[0], P.data[1].data[1] }, + fixed_power, + R); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.cpp b/test/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.cpp new file mode 100644 index 000000000..9ee6868c7 --- /dev/null +++ b/test/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.cpp @@ -0,0 +1,138 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_pairing_mnt4_298 + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; +using namespace blueprint::components::detail; + +template +void test_mnt4_298_exponentiation(std::vector public_input, + std::vector expected_res) { + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 8; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment; + + using value_type = typename FieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::mnt4_exponentiation; + + typename component_type::input_type instance_input = { + var(0,0, false, var::column_type::public_input), // f[0] + var(0,1, false, var::column_type::public_input), // f[1] + var(0,2, false, var::column_type::public_input), // f[2] + var(0,3, false, var::column_type::public_input), // f[3] + }; + + auto result_check = [&expected_res](AssignmentType const& assignment, + typename component_type::result_type const& real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "MNT4-298 Final exponentiation: expected res VS output\n"; + for(std::size_t i = 0; i < 4; i++) { + std::cout << std::dec << expected_res[i].data << " =? " << var_value(assignment, real_res.output[i]).data << "\n"; + } + #endif + for(std::size_t i = 0; i < 4; i++) { + assert(expected_res[i] == var_value(assignment, real_res.output[i])); + } + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance(witnesses, // witnesses + std::array{}, // constants + std::array{} // public inputs + ); + + nil::crypto3::test_component ( + component_instance, desc, public_input, result_check, instance_input); +} + +static const std::size_t random_tests_amount = 5; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt4_pairing_test) { + using curve_type = crypto3::algebra::curves::mnt4_298; + using gt_group_type = typename curve_type::gt_type; + using base_field_value = curve_type::base_field_type::value_type; + using field_type = typename curve_type::gt_type::base_field_type; + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + std::vector + AB_ML = { + 0x01f3f02a39499cca91c7c3a108cc0721047455bc2def95bcb613a1749c1bbe0fb0d88088699b_cppui298, + 0x00eeaea28cc850898e84e44ec6ae59fb0079c1d1c93f0f6e30c541695c45c4b9e3df07db6b77_cppui298, + 0x030ffc05991f5ac550b3c37b44c253e2c2a39359ec30d0ec24f4c42e3a6b937d02653c92e7d5_cppui298, + 0x001e339f66ccb68e51bab41310a5d30d2f51a002ae9b7bcf506c0c44447c725f0e2b618e9571_cppui298 + }, + AB_FE = { + 0x01fbabaf3b011714d2d119340016213db9fb8b5eeefbf32a082b4f8ee40cb6f79825d082f2b0_cppui298, + 0x038ae7fa7f91cc2ab005a8ea5c8c475820d848c5f0aa942a27da982b18fe96514c682a9bb227_cppui298, + 0x00fed8ea05b7600bea786cb3bb779876cb8623e11466112530237b9a2d296b4367033969b515_cppui298, + 0x008957e24bff45f132925d25383fbd0c1e1a0bea95fdae5346b0f42b2b44fbe3a80ce4a49d98_cppui298 + }; + + std::cout << "MNT4-298 Final exponentiation test\n"; + test_mnt4_298_exponentiation(AB_ML, AB_FE); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/algebra/pairing/weierstrass/plonk/mnt4_pairing.cpp b/test/algebra/pairing/weierstrass/plonk/mnt4_pairing.cpp new file mode 100644 index 000000000..9c3e6947d --- /dev/null +++ b/test/algebra/pairing/weierstrass/plonk/mnt4_pairing.cpp @@ -0,0 +1,147 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_pairing_mnt4_298 + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; +using namespace blueprint::components::detail; + +template +void test_mnt4_298_miller_loop(std::vector public_input, + std::vector expected_res) { + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = (WitnessColumns == 12)? (4 + 8) : (4 + 9); + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment; + + using value_type = typename FieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::mnt4_miller_loop; + + typename component_type::input_type instance_input = { + var(0,0, false, var::column_type::public_input), // xP + var(0,1, false, var::column_type::public_input), // yP + var(0,2, false, var::column_type::public_input), // xQ[0] + var(0,3, false, var::column_type::public_input), // xQ[1] + var(0,4, false, var::column_type::public_input), // yQ[0] + var(0,5, false, var::column_type::public_input) // yQ[1] + }; + + auto result_check = [&expected_res](AssignmentType const& assignment, + typename component_type::result_type const& real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "MNT4-298 Miller loop: expected res VS output\n"; + for(std::size_t i = 0; i < 4; i++) { + std::cout << std::dec << expected_res[i].data << " =? " << var_value(assignment, real_res.output[i]).data << "\n"; + } + #endif + for(std::size_t i = 0; i < 4; i++) { + assert(expected_res[i] == var_value(assignment, real_res.output[i])); + } + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance(witnesses, // witnesses + std::array{0}, // constants + std::array{} // public inputs + ); + + nil::crypto3::test_component ( + component_instance, desc, public_input, result_check, instance_input); +} + +static const std::size_t random_tests_amount = 5; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt4_pairing_test) { + using curve_type = crypto3::algebra::curves::mnt4_298; + using g2_group_type = typename curve_type::g2_type<>; + using base_field_value = curve_type::base_field_type::value_type; + using field_type = typename curve_type::g2_type<>::field_type::base_field_type; + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + std::vector + AB = { + // A :[ + 0x02ee42725289d05230b6700ba1696044a839c30e114c65ab98d5d0764b9c79c06d207c5f3d12_cppui298, + 0x0377ea5dd66341cb88b291c103eec911946c7266fabbda487ef3ac48e5954253df56f89ebe49_cppui298, + // ] + + // B :[ [ + 0x012212f4ac5a2b6262dcd15a0fb4e54d276d734d80e3868dc93a074b3a9ebeb598641aa2310d_cppui298, + 0x017600e8757679e06b66de2c48b3370e582443d4c0091ef1e6d96dadb92150ff642709dd806b_cppui298, + 0x02a1135b45f576b0988c2f5e852def5e829508beddae07427cc68929ffbeaa49de4d370cfa69_cppui298, + 0x0246c479956c92096a1dfa7cdb992b53ecb05f96d581fcb755045898fb459fd569753da2c2a7_cppui298 + // ]] + }, + AB_ML = { + 0x01f3f02a39499cca91c7c3a108cc0721047455bc2def95bcb613a1749c1bbe0fb0d88088699b_cppui298, + 0x00eeaea28cc850898e84e44ec6ae59fb0079c1d1c93f0f6e30c541695c45c4b9e3df07db6b77_cppui298, + 0x030ffc05991f5ac550b3c37b44c253e2c2a39359ec30d0ec24f4c42e3a6b937d02653c92e7d5_cppui298, + 0x001e339f66ccb68e51bab41310a5d30d2f51a002ae9b7bcf506c0c44447c725f0e2b618e9571_cppui298 + }; + + std::cout << "MNT4-298 Miller loop test\n"; + test_mnt4_298_miller_loop(AB, AB_ML); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/test_plonk_component.hpp b/test/test_plonk_component.hpp index b51a5a369..6f2e0724f 100644 --- a/test/test_plonk_component.hpp +++ b/test/test_plonk_component.hpp @@ -57,7 +57,7 @@ #include -// #include "profiling_plonk_circuit.hpp" +//#include "profiling_plonk_circuit.hpp" #include #include @@ -253,6 +253,14 @@ namespace nil { } } + std::ofstream fass("circuit.tbl"); + assignment.export_table(fass); + fass.close(); + + std::ofstream fcirc("circuit.crt"); + bp.export_circuit(fcirc); + fcirc.close(); + result_check(assignment, component_result); if constexpr (!PrivateInput) { @@ -309,15 +317,16 @@ namespace nil { } desc.rows_amount = zk::snark::basic_padding(assignment); -#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED +//#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED std::cout << "Usable rows: " << desc.usable_rows_amount << std::endl; std::cout << "Padded rows: " << desc.rows_amount << std::endl; - profiling(assignment); -#endif +// profiling(assignment); +//#endif //assignment.export_table(std::cout); //bp.export_circuit(std::cout); + assert(blueprint::is_satisfied(bp, assignment) == expected_to_pass); return std::make_tuple(desc, bp, assignment); @@ -621,4 +630,4 @@ namespace nil { } // namespace crypto3 } // namespace nil -#endif // CRYPTO3_TEST_PLONK_COMPONENT_HPP \ No newline at end of file +#endif // CRYPTO3_TEST_PLONK_COMPONENT_HPP