diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index a81b58081..adc802716 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -78,6 +78,8 @@ jobs: blueprint_verifiers_placeholder_f1_loop_test, blueprint_verifiers_placeholder_f3_loop_test, blueprint_verifiers_placeholder_gate_component_test, + blueprint_algebra_curves_plonk_bls12_g2_test, + blueprint_algebra_fields_plonk_non_native_fp12_arithmetic_test, blueprint_proxy_test blueprint_mock_mocked_components_test ] # Tests to execute diff --git a/include/nil/blueprint/components/algebra/curves/detail/plonk/bls12_g2_point_addition.hpp b/include/nil/blueprint/components/algebra/curves/detail/plonk/bls12_g2_point_addition.hpp new file mode 100644 index 000000000..149f36091 --- /dev/null +++ b/include/nil/blueprint/components/algebra/curves/detail/plonk/bls12_g2_point_addition.hpp @@ -0,0 +1,365 @@ +//---------------------------------------------------------------------------// +// 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 point addition in the elliptic +// curve group G2 = E'(F_p^2) : y^2 = x^3 + 4(1+u) with +// F_p^2 = F_p[u]/(u^2 - (-1)). +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BLS12_G2_POINT_ADDITION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BLS12_G2_POINT_ADDITION_HPP + +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + // E'(F_p^2) : y^2 = x^3 + 4(1+u) 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 bls12_g2_point_addition; + + template + class bls12_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 bls12_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; + } + }; + + struct result_type { + std::array R; + + result_type(const bls12_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 bls12_g2_point_addition(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + bls12_g2_point_addition(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + bls12_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_bls12_g2_point_addition = + bls12_g2_point_addition< + crypto3::zk::snark::plonk_constraint_system>; + + template + typename plonk_bls12_g2_point_addition::result_type generate_assignments( + const plonk_bls12_g2_point_addition &component, + assignment> + &assignment, + const typename plonk_bls12_g2_point_addition::input_type + &instance_input, + const std::uint32_t start_row_index) { + + 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) / (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_bls12_g2_point_addition::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_bls12_g2_point_addition &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bls12_g2_point_addition::input_type + &instance_input) { + + const std::size_t WA = component.witness_amount(); + + using var = typename plonk_bls12_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 cnstr_zero = constraint_type(), + cnstr_one = cnstr_zero + 1; + + fp2_constraint one = {cnstr_one,cnstr_zero}, + 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)) = 0 (15) + C = yQ * (2*yP * zPQ * ((xP - xQ)*la - (yP - yQ)) + (one - (xP - xQ)*zPQ) * wPQ *(2*yP*la - 3*xP*xP)); + Cs.push_back(C[0]); Cs.push_back(C[1]); + + return bp.add_gate(Cs); + } + + template + void generate_copy_constraints( + const plonk_bls12_g2_point_addition &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bls12_g2_point_addition::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_bls12_g2_point_addition::var; + + 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_bls12_g2_point_addition::result_type generate_circuit( + const plonk_bls12_g2_point_addition &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bls12_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_bls12_g2_point_addition::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BLS12_G2_POINT_ADDITION_HPP diff --git a/include/nil/blueprint/components/algebra/curves/detail/plonk/bls12_g2_point_double.hpp b/include/nil/blueprint/components/algebra/curves/detail/plonk/bls12_g2_point_double.hpp new file mode 100644 index 000000000..a1e4a5c64 --- /dev/null +++ b/include/nil/blueprint/components/algebra/curves/detail/plonk/bls12_g2_point_double.hpp @@ -0,0 +1,269 @@ +//---------------------------------------------------------------------------// +// 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 point doubling in the elliptic +// curve group G2 = E'(F_p^2) : y^2 = x^3 + 4(1+u) with +// F_p^2 = F_p[u]/(u^2 - (-1)). +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BLS12_G2_POINT_DOUBLE_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BLS12_G2_POINT_DOUBLE_HPP + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + // E'(F_p^2) : y^2 = x^3 + 4(1+u) point doubling gate. + // Expects point at infinity encoded by (0,0) in input, outputs (0,0) for its double + // Input: (xP, yP) = P[4] + // Output: (xR, yR) = R[4], R = [2]P as element of E'(F_p^2) + + template + class bls12_g2_point_double; + + template + class bls12_g2_point_double> + : 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 bls12_g2_point_double::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_single_value_param(10)), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t lookup_column_amount) { + return 1; + } + + 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; + + std::vector> all_vars() { + std::vector> res = {}; + for(auto & e : P) { res.push_back(e); } + return res; + } + }; + + struct result_type { + std::array R; + + result_type(const bls12_g2_point_double &component, std::uint32_t start_row_index) { + for(std::size_t i = 0; i < 4; i++) { + R[i] = var(component.W(i+6), start_row_index, false, var::column_type::witness); + } + } + + std::vector> all_vars() { + std::vector> res = {}; + + for(auto & e : R) { res.push_back(e); } + return res; + } + }; + + template + explicit bls12_g2_point_double(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + bls12_g2_point_double(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + bls12_g2_point_double( + 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_g2_point_double = + bls12_g2_point_double>; + + template + typename plonk_bls12_g2_point_double::result_type generate_assignments( + const plonk_bls12_g2_point_double &component, + assignment> + &assignment, + const typename plonk_bls12_g2_point_double::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using policy_type_fp2 = crypto3::algebra::fields::fp2; + using fp2_element = typename policy_type_fp2::value_type; + + fp2_element 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])), + lambda = 3*xP.pow(2) / (2*yP), // division by 0 is defined as 0 + nu = yP - lambda*xP, + xR = lambda.pow(2) - 2*xP, + yR = -(lambda*xR + nu), + zero_check = yP.inversed(); + + 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) = zero_check.data[i]; + assignment.witness(component.W(6 + i),start_row_index) = xR.data[i]; + assignment.witness(component.W(8 + i),start_row_index) = yR.data[i]; + } + + return typename plonk_bls12_g2_point_double::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_bls12_g2_point_double &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bls12_g2_point_double::input_type + &instance_input) { + + using var = typename plonk_bls12_g2_point_double::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + using fp2_constraint = detail::abstract_fp2_element; + + constraint_type cnstr_zero = constraint_type(), + cnstr_one = cnstr_zero + 1; + + fp2_constraint one = {cnstr_one,cnstr_zero}, + 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)}, + ZC = {var(component.W(4), 0, true),var(component.W(5), 0, true)}, + xR = {var(component.W(6), 0, true),var(component.W(7), 0, true)}, + yR = {var(component.W(8), 0, true),var(component.W(9), 0, true)}; + fp2_constraint C1, C2, C3, C4; + + // the defining equations are + // xR = (3xP^2 / 2yP)^2 - 2xP + // yR = - (3xP^2 / 2yP) xR - yP + (3xP^2 / 2yP)xP + // We transform them into constraints: + // (2yP)^2 (xR + 2xP) - (3xP^2)^2 = 0 + // (2yP) (yR + yP) + (3xP^2)(xR - xP) = 0 + // Additional constraint to assure that the double of (0,0) is (0,0). + // If yP is 0, xR and yR are forced to be zero. For a non-zero yP + // yR cannot be zero either => ZC is the inverse of yP + // (ZC * yP - 1) * xR = 0 + // (ZC * yP - 1) * yR = 0 + + C1 = (2*yP) * (2*yP) * (xR + 2*xP) - (3*xP*xP)*(3*xP*xP); + C2 = (2*yP) * (yR + yP) + (3*xP*xP)*(xR - xP); + C3 = (ZC*yP - one)*xR; + C4 = (ZC*yP - one)*yR; + + std::vector Cs = {}; + for(std::size_t i = 0; i < 2; i++) { + Cs.push_back(C1[i]); + Cs.push_back(C2[i]); + Cs.push_back(C3[i]); + Cs.push_back(C4[i]); + } + return bp.add_gate(Cs); + } + + template + void generate_copy_constraints( + const plonk_bls12_g2_point_double &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bls12_g2_point_double::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_bls12_g2_point_double::var; + + 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_bls12_g2_point_double::result_type generate_circuit( + const plonk_bls12_g2_point_double &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_bls12_g2_point_double::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_bls12_g2_point_double::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_BLS12_G2_POINT_DOUBLE_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp12.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp12.hpp new file mode 100644 index 000000000..5df6e4923 --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp12.hpp @@ -0,0 +1,132 @@ +//---------------------------------------------------------------------------// +// 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 F_p^{12} elements over ab abstract entity (to be used with constraints). +// 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_ABSTRACT_FP12_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP12_HPP + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + + // actually compute all bilinear forms that represent multiplication in F_p^12 + template + std::array perform_fp12_mult(std::array a, std::array b) { + std::array c; + + for(std::size_t i = 0; i < 12; i++) { + c[i] = T(); // assume default constructor creates a "zero" object which is true for constraints and numbers + } + + for(std::size_t i = 0; i < 12; i++) { + for(std::size_t j = 0; j < 12; j++) { + std::size_t dw = i/6 + j/6; + std::size_t dv = (i % 6)/2 + (j % 6)/2; + std::size_t du = (i % 2) + (j % 2); + + if (dw == 2) { + // reduction according to w^2 = v + dw = 0; dv++; + } + // possible change of sign according to u^2 = -1 + // NB: the only reason for having this "if" (and the one several lines below) + // instead of possibly multiplying the product a[i]*b[j] by (-1), is to + // have constraints that are written shorter, as opposed to the ones that contain -1. + // Because -1 is a number with a lot of digits in F_p. + if (du > 1) { + c[6*dw + 2*(dv % 3) + (du % 2)] -= a[i] * b[j]; + } else { + c[6*dw + 2*(dv % 3) + (du % 2)] += a[i] * b[j]; + } + if (dv > 2) { + // reduction according to v^3 = u + 1 + dv -= 3; du++; + // account for u in the reduction v^3 = u + 1 + if (du > 1) { + c[6*dw + 2*dv + (du % 2)] -= a[i] * b[j]; + } else { + c[6*dw + 2*dv + (du % 2)] += a[i] * b[j]; + } + } + } + } + return c; + } + template + class abstract_fp12_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_fp12_element operator*(const abstract_fp12_element& other) { + std::array res = perform_fp12_mult(data,other.data); + return { res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7], res[8], res[9], res[10], res[11] }; + } + constexpr abstract_fp12_element operator*(const int x) { + std::array res; + for(std::size_t i = 0; i < 12; i++) { + res[i] = data[i] * x; + } + return { res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7], res[8], res[9], res[10], res[11] }; + } + friend abstract_fp12_element operator*(const int x, const abstract_fp12_element& e) { + std::array res; + for(std::size_t i = 0; i < 12; i++) { + res[i] = e[i] * x; + } + return { res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7], res[8], res[9], res[10], res[11] }; + } + constexpr abstract_fp12_element operator+(const abstract_fp12_element& other) { + std::array res; + for(std::size_t i = 0; i < 12; i++) { + res[i] = data[i] + other.data[i]; + } + return { res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7], res[8], res[9], res[10], res[11] }; + } + constexpr abstract_fp12_element operator-(const abstract_fp12_element& other) { + std::array res; + for(std::size_t i = 0; i < 12; i++) { + res[i] = data[i] - other.data[i]; + } + return { res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7], res[8], res[9], res[10], res[11] }; + } + }; + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP12_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 new file mode 100644 index 000000000..cab16a84f --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp2.hpp @@ -0,0 +1,71 @@ +//---------------------------------------------------------------------------// +// 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 F_p^2 elements over an abstract entity (to be used with constraints) +// with F_p^2 = F_p[u]/(u^2 - (-1)). +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP2_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP2_HPP + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + template + class abstract_fp2_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_fp2_element operator*(const abstract_fp2_element& other) { + return { data[0] * other.data[0] - data[1] * other.data[1], + data[0] * other.data[1] + data[1] * other.data[0]}; + } + constexpr abstract_fp2_element operator*(const int x) { + return { data[0]*x, data[1]*x }; + } + friend abstract_fp2_element operator*(const int x, const abstract_fp2_element& e) { + 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] }; + } + constexpr abstract_fp2_element operator-(const abstract_fp2_element& other) { + return { data[0] - other.data[0], data[1] - other.data[1] }; + } + }; + + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP2_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/fp12_frobenius_map.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/fp12_frobenius_map.hpp new file mode 100644 index 000000000..43871ce42 --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/fp12_frobenius_map.hpp @@ -0,0 +1,363 @@ +//---------------------------------------------------------------------------// +// 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} computation of p^k (k = 1,2,3) +// as a unary operation x[12] -> y[12], y = x^R +// 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_FROBENIUS_MAP_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FP12_FROBENIUS_MAP_HPP + +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + enum small_p_power {p_one = 1, p_two = 2, p_three = 3}; + + template + std::array get_Fp12_frobenius_coefficients(small_p_power Power) { + + using policy_type_fp2 = crypto3::algebra::fields::fp2; + using fp2_element = typename policy_type_fp2::value_type; + + std::array res; + + if constexpr (std::is_same_v>) { + // for BLS12-381 we have all the constants precomputed + if (Power == p_one) { + res[0] = 0x1_cppui381; + res[1] = 0x0_cppui381; + res[2] = 0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8_cppui381; + res[3] = 0xfc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3_cppui381; + res[4] = 0x0_cppui381; + res[5] = 0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac_cppui381; + res[6] = 0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09_cppui381; + res[7] = 0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09_cppui381; + res[8] = 0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad_cppui381; + res[9] = 0x0_cppui381; + res[10] = 0x5b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116_cppui381; + res[11] = 0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995_cppui381; + } else if (Power == p_two) { + res[0] = 0x1_cppui381; + res[1] = 0x0_cppui381; + res[2] = 0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff_cppui381; + res[3] = 0x0_cppui381; + res[4] = 0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe_cppui381; + res[5] = 0x0_cppui381; + res[6] = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa_cppui381; + res[7] = 0x0_cppui381; + res[8] = 0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac_cppui381; + res[9] = 0x0_cppui381; + res[10] = 0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad_cppui381; + res[11] = 0x0_cppui381; + } else { + res[0] = 0x1_cppui381; + res[1] = 0x0_cppui381; + res[2] = 0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2_cppui381; + res[3] = 0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09_cppui381; + res[4] = 0x0_cppui381; + res[5] = 0x1_cppui381; + res[6] = 0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2_cppui381; + res[7] = 0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2_cppui381; + res[8] = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa_cppui381; + res[9] = 0x0_cppui381; + res[10] = 0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09_cppui381; + res[11] = 0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2_cppui381; + } + } else { + // otherwise fallback to computation of constants + typename BlueprintFieldType::integral_type field_p = BlueprintFieldType::modulus, + coef_exp = (field_p - 1)/6; + fp2_element frob_coef = fp2_element::one(), + u_plus_1_pow = fp2_element(1,1).pow(coef_exp); + int k = int(Power); + + for(std::size_t i = 0; i < 6; i++) { + res[2*i] = frob_coef.data[0]; + res[2*i+1] = frob_coef.data[1]; + frob_coef *= u_plus_1_pow; + } + + if (k > 1) { + std::array gamma_2; + + for(std::size_t i = 0; i < 6; i++) gamma_2[i] = res[2*i].pow(2) + res[2*i+1].pow(2); + + if (k > 2) { + for(std::size_t i = 0; i < 6; i++) { + res[2*i] *= gamma_2[i]; + res[2*i+1] *= gamma_2[i]; + } + } else { + res.fill(BlueprintFieldType::value_type::zero()); + for(std::size_t i = 0; i < 6; i++) { + res[2*i] = gamma_2[i]; + } + } + } + } + return res; + } + } // namespace detail + + // F_p^12 gate for computing Frobenius maps x -> x^{p^k} for small k = 1,2,3 + // Parameter: Power = 1,2,3 + // Input: x[12], x + // Output: y[12], y = x^{p^k} as elements of F_p^12 + + using namespace detail; + + template + class fp12_frobenius_map; + + template + class fp12_frobenius_map, Power> + : 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 fp12_frobenius_map::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)), // from 12 to 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); // anything that's smaller than 24 columns wide requires 2 rows + } + + 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() { + std::vector> res = {}; + for(auto & e : x) { res.push_back(e); } + return res; + } + }; + + struct result_type { + std::array output; + + result_type(const fp12_frobenius_map &component, std::uint32_t start_row_index) { + const std::size_t WA = component.witness_amount(); + + for(std::size_t i = 0; i < 12; i++) { + output[i] = var(component.W((i+12) % WA), start_row_index + (i+12)/WA, 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_frobenius_map(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + fp12_frobenius_map(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + fp12_frobenius_map( + 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_frobenius_map = + fp12_frobenius_map, Power>; + + template + typename plonk_fp12_frobenius_map::result_type generate_assignments( + const plonk_fp12_frobenius_map &component, + assignment> + &assignment, + const typename plonk_fp12_frobenius_map::input_type + &instance_input, + const std::uint32_t start_row_index) { + + 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 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; + + for(std::size_t i = 0; i < Power; i++) { + Y = Y.pow(field_p); + } + + for(std::size_t i = 0; i < 12; i++) { + assignment.witness(component.W((12 + i) % WA),start_row_index + (12 + i)/WA) = Y.data[i/6].data[(i % 6)/2].data[i % 2]; + } + + return typename plonk_fp12_frobenius_map::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_fp12_frobenius_map &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_frobenius_map::input_type + &instance_input) { + + using var = typename plonk_fp12_frobenius_map::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + const std::size_t WA = component.witness_amount(); + + std::array X, Y, P; + std::array,6> Z; + std::array C; + + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W(i), 0, true); + Y[i] = var(component.W((i+12) % WA), (i+12)/WA, true); + } + + C = 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² + Z[i][0] = X[2*(i/2 + 3*(i % 2))]; + // Fp2 elements are conjugated when raising to p and p^3 + Z[i][1] = X[2*(i/2 + 3*(i % 2)) + 1] * (Power != p_two ? -1 : 1); + + P[2*(i/2 + 3*(i % 2))] = Z[i][0]*C[2*i] - Z[i][1]*C[2*i+1]; + P[2*(i/2 + 3*(i % 2)) + 1] = Z[i][0]*C[2*i+1] + Z[i][1]*C[2*i]; + } + + std::vector Cs = {}; + for(std::size_t i = 0; i < 12; i++) { + Cs.push_back(P[i] - Y[i]); + } + + return bp.add_gate(Cs); + } + + template + void generate_copy_constraints( + const plonk_fp12_frobenius_map &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_frobenius_map::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_fp12_frobenius_map::var; + + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({var(component.W(i), start_row_index, false), instance_input.x[i]}); + } + } + + template + typename plonk_fp12_frobenius_map::result_type generate_circuit( + const plonk_fp12_frobenius_map &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_frobenius_map::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_fp12_frobenius_map::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FP12_FROBENIUS_MAP_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/fp12_inversion.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/fp12_inversion.hpp new file mode 100644 index 000000000..739dca7a8 --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/fp12_inversion.hpp @@ -0,0 +1,252 @@ +//---------------------------------------------------------------------------// +// 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} field inversion. +// 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_INVERSION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FP12_INVERSION_HPP + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + // F_p^12 inversion gate + // Input: x[12], x != 0 + // Output: y[12]: x*y = 1 as elements of F_p^12 + + template + class fp12_inversion; + + template + class fp12_inversion> + : 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 fp12_inversion::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)), // from 12 to 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); // anything that's smaller than 24 columns wide requires 2 rows + } + + 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() { + std::vector> res = {}; + for(auto & e : x) { res.push_back(e); } + return res; + } + }; + + struct result_type { + std::array output; + + result_type(const fp12_inversion &component, std::uint32_t start_row_index) { + const std::size_t WA = component.witness_amount(); + + for(std::size_t i = 0; i < 12; i++) { + output[i] = var(component.W((i+12) % WA), start_row_index + (i+12)/WA, 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_inversion(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + fp12_inversion(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + fp12_inversion( + 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_inversion = + fp12_inversion>; + + template + typename plonk_fp12_inversion::result_type generate_assignments( + const plonk_fp12_inversion &component, + assignment> + &assignment, + const typename plonk_fp12_inversion::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 == fp12_element::zero())? fp12_element::zero() : X.inversed(); // if X == 0, we fail with Y=0 + + for(std::size_t i = 0; i < 12; i++) { + assignment.witness(component.W((12 + i) % WA),start_row_index + (12 + i)/WA) = Y.data[i/6].data[(i % 6)/2].data[i % 2]; + } + + return typename plonk_fp12_inversion::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_fp12_inversion &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_inversion::input_type + &instance_input) { + + using var = typename plonk_fp12_inversion::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + using fp12_constraint = detail::abstract_fp12_element; + + const std::size_t WA = component.witness_amount(); + + fp12_constraint X, Y, C; + + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W(i), 0, true); + Y[i] = var(component.W((i+12) % WA), (i+12)/WA, true); + } + C = X * Y; + + std::vector Cs = { C[0] - 1 }; + for(std::size_t i = 1; i < 12; i++) { + Cs.push_back(C[i]); + } + + return bp.add_gate(Cs); + } + + template + void generate_copy_constraints( + const plonk_fp12_inversion &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_inversion::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_fp12_inversion::var; + + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({var(component.W(i), start_row_index, false), instance_input.x[i]}); + } + } + + template + typename plonk_fp12_inversion::result_type generate_circuit( + const plonk_fp12_inversion &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_inversion::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_fp12_inversion::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FP12_INVERSION_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/fp12_multiplication.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/fp12_multiplication.hpp new file mode 100644 index 000000000..1ebaa3b6c --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/fp12_multiplication.hpp @@ -0,0 +1,260 @@ +//---------------------------------------------------------------------------// +// 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} field multiplication. +// 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_MULTIPLICATION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FP12_MULTIPLICATION_HPP + +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + // F_p^12 multiplication gate + // Input: a[12], b[12] + // Output: c[12] = a*b as elements of F_p^12 + + template + class fp12_multiplication; + + template + class fp12_multiplication> + : 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 fp12_multiplication::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,36)), // from 12 to 36 + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount(std::size_t witness_amount, + std::size_t lookup_column_amount) { + return 36/witness_amount + (36 % witness_amount > 0); + } + + 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 a; + std::array b; + + std::vector> all_vars() { + std::vector> res = {}; + for(auto & e : a) { res.push_back(e); } + for(auto & e : b) { res.push_back(e); } + return res; + } + }; + + struct result_type { + std::array output; + + result_type(const fp12_multiplication &component, std::uint32_t start_row_index) { + const std::size_t WA = component.witness_amount(); + + for(std::size_t i = 0; i < 12; i++) { + output[i] = var(component.W((i+24) % WA), start_row_index + (i+24)/WA, 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_multiplication(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + fp12_multiplication(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + fp12_multiplication( + 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_multiplication = + fp12_multiplication>; + + template + typename plonk_fp12_multiplication::result_type generate_assignments( + const plonk_fp12_multiplication &component, + assignment> + &assignment, + const typename plonk_fp12_multiplication::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 a; + std::array b; + + for(std::size_t i = 0; i < 12; i++) { + a[i] = var_value(assignment, instance_input.a[i]); + b[i] = var_value(assignment, instance_input.b[i]); + assignment.witness(component.W(i),start_row_index) = a[i]; + assignment.witness(component.W((12 + i) % WA),start_row_index + (12 + i)/WA) = b[i]; + } + + using policy_type_fp12 = crypto3::algebra::fields::fp12_2over3over2; + using fp12_element = typename policy_type_fp12::value_type; + + fp12_element A = fp12_element({ {a[0],a[1]}, {a[2],a[3]}, {a[4],a[5]} }, { {a[6],a[7]}, {a[8],a[9]}, {a[10],a[11]} }), + B = fp12_element({ {b[0],b[1]}, {b[2],b[3]}, {b[4],b[5]} }, { {b[6],b[7]}, {b[8],b[9]}, {b[10],b[11]} }), + C = A*B; + + for(std::size_t i = 0; i < 12; i++) { + assignment.witness(component.W((24 + i) % WA),start_row_index + (24 + i)/WA) = C.data[i/6].data[(i % 6)/2].data[i % 2]; + } + + return typename plonk_fp12_multiplication::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_fp12_multiplication &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_multiplication::input_type + &instance_input) { + + using var = typename plonk_fp12_multiplication::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + using fp12_constraint = detail::abstract_fp12_element; + + const std::size_t WA = component.witness_amount(); + const int shift = -(WA < 24); // if WA is small we use 3 rows, and need to shift everything + + fp12_constraint A, B, C; + + for(std::size_t i = 0; i < 12; i++) { + A[i] = var(component.W(i), 0 + shift, true); + B[i] = var(component.W((i+12) % WA), (i+12)/WA + shift, true); + } + C = A * B; + + std::vector Cs = {}; + for(std::size_t i = 0; i < 12; i++) { + Cs.push_back(C[i] - var(component.W((i+24) % WA), (i+24)/WA + shift, true)); + } + + return bp.add_gate(Cs); + } + + template + void generate_copy_constraints( + const plonk_fp12_multiplication &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_multiplication::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_fp12_multiplication::var; + + const std::size_t WA = component.witness_amount(); + + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({var(component.W(i), start_row_index, false), instance_input.a[i]}); + bp.add_copy_constraint({var(component.W((12 + i) % WA), start_row_index + (12 + i)/WA, false), instance_input.b[i]}); + } + } + + template + typename plonk_fp12_multiplication::result_type generate_circuit( + const plonk_fp12_multiplication &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_multiplication::input_type &instance_input, + const std::size_t start_row_index) { + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + + // if less then 24 witness columns are used, we apply the gate to the second row + assignment.enable_selector(selector_index, start_row_index + (component.witness_amount() < 24)); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_fp12_multiplication::result_type( + component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FP12_MULTIPLICATION_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/fp12_small_power.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/fp12_small_power.hpp new file mode 100644 index 000000000..b78f45cd5 --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/fp12_small_power.hpp @@ -0,0 +1,275 @@ +//---------------------------------------------------------------------------// +// 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} computation of small powers (2,3,4) +// as a unary operation x[12] -> y[12], y = x^R +// 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_SMALL_POWER_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FP12_SMALL_POWER_HPP + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + enum small_power {square = 2, cube = 3, power4 = 4}; + } // namespace detail + + // F_p^12 gate for computing small powers (2,3,4) + // Parameter: Power = 2,3,4 + // Input: x[12], x + // Output: y[12], y = x^Power as elements of F_p^12 + + using namespace detail; + + template + class fp12_small_power; + + template + class fp12_small_power, Power> + : 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 fp12_small_power::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)), // from 12 to 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); // anything that's smaller than 24 columns wide requires 2 rows + } + + 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() { + std::vector> res = {}; + for(auto & e : x) { res.push_back(e); } + return res; + } + }; + + struct result_type { + std::array output; + + result_type(const fp12_small_power &component, std::uint32_t start_row_index) { + const std::size_t WA = component.witness_amount(); + + for(std::size_t i = 0; i < 12; i++) { + output[i] = var(component.W((i+12) % WA), start_row_index + (i+12)/WA, 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_small_power(ContainerType witness) : component_type(witness, {}, {}, get_manifest()) {}; + + template + fp12_small_power(WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + fp12_small_power( + 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_small_power = + fp12_small_power< + crypto3::zk::snark::plonk_constraint_system, Power>; + + template + typename plonk_fp12_small_power::result_type generate_assignments( + const plonk_fp12_small_power &component, + assignment> + &assignment, + const typename plonk_fp12_small_power::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.pow(int(Power)); + + for(std::size_t i = 0; i < 12; i++) { + assignment.witness(component.W((12 + i) % WA),start_row_index + (12 + i)/WA) = Y.data[i/6].data[(i % 6)/2].data[i % 2]; + } + + return typename plonk_fp12_small_power::result_type( + component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_fp12_small_power &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_small_power::input_type + &instance_input) { + + using var = typename plonk_fp12_small_power::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + + using fp12_constraint = detail::abstract_fp12_element; + + const std::size_t WA = component.witness_amount(); + + fp12_constraint X, Y, C; + + for(std::size_t i = 0; i < 12; i++) { + X[i] = var(component.W(i), 0, true); + Y[i] = var(component.W((i+12) % WA), (i+12)/WA, true); + } + + C = X * X; + switch(Power) { + case square: { + break; + } + case cube: { + C = C * X; // 3 + break; + } + case power4: { + C = C * C; // 4 + break; + } + } + + std::vector Cs = {}; + for(std::size_t i = 1; i < 12; i++) { + Cs.push_back(C[i] - Y[i]); + } + + return bp.add_gate(Cs); + } + + template + void generate_copy_constraints( + const plonk_fp12_small_power &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_small_power::input_type &instance_input, + const std::size_t start_row_index) { + + using var = typename plonk_fp12_small_power::var; + + for(std::size_t i = 0; i < 12; i++) { + bp.add_copy_constraint({var(component.W(i), start_row_index, false), instance_input.x[i]}); + } + } + + template + typename plonk_fp12_small_power::result_type generate_circuit( + const plonk_fp12_small_power &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_fp12_small_power::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_fp12_small_power::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_FP12_SMALL_POWER_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..d875af07d --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/detail/fp12_power_t.hpp @@ -0,0 +1,434 @@ +//---------------------------------------------------------------------------// +// 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) { + 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 + } + + 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 x; + + std::vector> all_vars() { + std::vector> res = {}; + for(auto & e : x) { res.push_back(e); } + return res; + } + }; + + 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/test/CMakeLists.txt b/test/CMakeLists.txt index e24e14030..851ce95d9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -76,6 +76,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/fp12_arithmetic" "non_native/plonk/bool_scalar_multiplication" "non_native/plonk/add_mul_zkllvm_compatible" "non_native/plonk/scalar_non_native_range" @@ -86,6 +87,7 @@ 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/bls12_g2" "hashes/plonk/poseidon" "hashes/plonk/sha256" "hashes/plonk/sha512" diff --git a/test/algebra/curves/plonk/bls12_g2.cpp b/test/algebra/curves/plonk/bls12_g2.cpp new file mode 100644 index 000000000..43125b610 --- /dev/null +++ b/test/algebra/curves/plonk/bls12_g2.cpp @@ -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. +//---------------------------------------------------------------------------// +// +// BLS12-381 g2 group operations tests +// +#define BOOST_TEST_MODULE blueprint_plonk_bls12_g2_test + +#include + +#include +#include + +#include + +#include + +#include +#include + +#include +#include + +#include "../../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_bls12_g2_doubling(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 = 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::bls12_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::template 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_bls12_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::bls12_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.pow(2), + expected_y = expected_res.Y / expected_res.Z.pow(3); + #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); +} + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_bls12_g2_test_381) { + using curve_type = crypto3::algebra::curves::bls12_381; + using group_type = typename curve_type::template 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("19354805336845174941142151562851080662656573665208680741935" + "4395577367693778571452628423727082668900187036482254730"), + integral_type("89193000964309942330810277795125089969455920364772498836102" + "2851024990473423938537113948850338098230396747396259901")), + field_value_type(integral_type("77171727205583415237828170597267125700535714547880090837365" + "9404991537354153455452961747174765859335819766715637138"), + integral_type("28103101185821266340411334541807053043930791391032529565024" + "04531123692847658283858246402311867775854528543237781718")), + field_value_type::one()), + group_value_type( + field_value_type(integral_type("424958340463073975547762735517193206833255107941790909009827635" + "556634414746056077714431786321247871628515967727334"), + integral_type("301867980397012787726282639381447252855741350432919474049536385" + "2840690589001358162447917674089074634504498585239512")), + field_value_type(integral_type("362130818512839545988899552652712755661476860447213217606042330" + "2734876099689739385100475320409412954617897892887112"), + integral_type("102447784096837908713257069727879782642075240724579670654226801" + "345708452018676587771714457671432122751958633012502")), + field_value_type::one()), + group_value_type( + field_value_type(integral_type("278579072823914661770244330824853538101603574852069839969013232" + "5213972292102741627498014391457605127656937478044880"), + integral_type("385570939363183188091016781827643518714796337112619879965480309" + "9743427431977934703201153169947378798970358200024876")), + field_value_type(integral_type("821938378705205565995357931232097952117504537366318395539093959" + "918654729488074273868834599496909844419980823111624"), + integral_type("180242033557577995098293558042145430208756792638522270794752735" + "3462942499437987207287862072369052390195154530059198")), + field_value_type::one()), + group_value_type( + field_value_type(integral_type("394904109851368845549123118074972479469719294319673003085328501" + "1755806989731870696216017360514887069032515603535834"), + integral_type("141689369450613197680900293521221631713294194257076384932306538" + "1335907430566747765697423320407614734575486820936593")), + field_value_type(integral_type("322745371086383503299296260585144940139139935513544272889379018" + "6263669279022343042444878900124369614767241382891922"), + integral_type("149873883407375987188646612293399676447188951453282792720277792" + "2460876335493588931070034160657995151627624577390178")), + field_value_type::one()), + group_value_type( + field_value_type(integral_type("254155017921606149907129844368549510385368618440139550318910532" + "874259603395336903946742408725761795820224536519988"), + integral_type("276843145929673042677916621854414979160158598623313058301150172" + "7704972362141149700714785450629498506208393873593705")), + field_value_type(integral_type("175533934474433745731856511606202566998475061793772124522071142" + "5551575490663761638802010265668157125441634554205566"), + integral_type("560643043433789571968941329642646582974304556331567393300563909" + "451776257854214387388500126524984624222885267024722")), + field_value_type::one())}; + + 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.pow(2); + py = P.Y / P.Z.pow(3); + } + // doubling test + std::cout << "Doubling\n"; + test_bls12_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_bls12_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); + + field_value_type qx = field_value_type::zero(), + qy = field_value_type::zero(); + + // test zero addition + std::cout << "Addition P + 0\n"; + test_bls12_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_bls12_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_bls12_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.pow(2); + qy = Q.Y / Q.Z.pow(3); + } + test_bls12_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/fields/plonk/non_native/fp12_arithmetic.cpp b/test/algebra/fields/plonk/non_native/fp12_arithmetic.cpp new file mode 100644 index 000000000..2f83e0c48 --- /dev/null +++ b/test/algebra/fields/plonk/non_native/fp12_arithmetic.cpp @@ -0,0 +1,469 @@ +//---------------------------------------------------------------------------// +// 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. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_fields_non_native_fp12_test + +#include + +#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_fp12_multiplication(std::vector public_input) { + 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 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_multiplication; + + typename component_type::input_type instance_input; + typename std::array A, B; + typename std::array expected_res; + + for(std::size_t i = 0; i < 12; i++) { + instance_input.a[i] = var(0,i, false, var::column_type::public_input); + instance_input.b[i] = var(0,i+12, false, var::column_type::public_input); + A[i] = public_input[i]; + B[i] = public_input[i+12]; + } + + using policy_type_fp12 = crypto3::algebra::fields::fp12_2over3over2; + using fp12_element = typename policy_type_fp12::value_type; + + fp12_element e1 = fp12_element({ {A[0],A[1]}, {A[2],A[3]}, {A[4],A[5]} }, { {A[6],A[7]}, {A[8],A[9]}, {A[10],A[11]} }), + e2 = fp12_element({ {B[0],B[1]}, {B[2],B[3]}, {B[4],B[5]} }, { {B[6],B[7]}, {B[8],B[9]}, {B[10],B[11]} }), + e = e1 * e2; // fp12 multiplication + + expected_res = { + e.data[0].data[0].data[0], e.data[0].data[0].data[1], + e.data[0].data[1].data[0], e.data[0].data[1].data[1], + e.data[0].data[2].data[0], e.data[0].data[2].data[1], + e.data[1].data[0].data[0], e.data[1].data[0].data[1], + e.data[1].data[1].data[0], e.data[1].data[1].data[1], + e.data[1].data[2].data[0], e.data[1].data[2].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 << "Fp12 multiplication res vs output\n"; + for(std::size_t i = 0; i < 12; 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 < 12; 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, + nil::blueprint::connectedness_check_type::type::STRONG); +} + +template +void test_fp12_inversion(std::vector public_input) { + 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 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_inversion; + + typename component_type::input_type instance_input; + typename std::array X; + typename std::array expected_res; + + for(std::size_t i = 0; i < 12; i++) { + instance_input.x[i] = var(0,i, false, var::column_type::public_input); + X[i] = public_input[i]; + } + + using policy_type_fp12 = crypto3::algebra::fields::fp12_2over3over2; + using fp12_element = typename policy_type_fp12::value_type; + + fp12_element e1 = 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]} }), + e = (e1 == fp12_element::zero())? fp12_element::zero() : e1.inversed(); // fp12 inversion + + const bool expected_to_pass = (e1 != fp12_element::zero()); + + expected_res = { + e.data[0].data[0].data[0], e.data[0].data[0].data[1], + e.data[0].data[1].data[0], e.data[0].data[1].data[1], + e.data[0].data[2].data[0], e.data[0].data[2].data[1], + e.data[1].data[0].data[0], e.data[1].data[0].data[1], + e.data[1].data[1].data[0], e.data[1].data[1].data[1], + e.data[1].data[2].data[0], e.data[1].data[2].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 << "Fp12 inversion res vs output\n"; + for(std::size_t i = 0; i < 12; 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 < 12; 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 + ); + + if (expected_to_pass) { + nil::crypto3::test_component ( + component_instance, desc,public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG); + } else { + nil::crypto3::test_component_to_fail ( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG); + } +} + +template +void test_fp12_small_power(std::vector public_input) { + 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 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_small_power; + + typename component_type::input_type instance_input; + typename std::array X; + typename std::array expected_res; + + for(std::size_t i = 0; i < 12; i++) { + instance_input.x[i] = var(0,i, false, var::column_type::public_input); + X[i] = public_input[i]; + } + + using policy_type_fp12 = crypto3::algebra::fields::fp12_2over3over2; + using fp12_element = typename policy_type_fp12::value_type; + + fp12_element e1 = 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]} }), + e = e1.pow(int(Power)); // fp12 power raising + + expected_res = { + e.data[0].data[0].data[0], e.data[0].data[0].data[1], + e.data[0].data[1].data[0], e.data[0].data[1].data[1], + e.data[0].data[2].data[0], e.data[0].data[2].data[1], + e.data[1].data[0].data[0], e.data[1].data[0].data[1], + e.data[1].data[1].data[0], e.data[1].data[1].data[1], + e.data[1].data[2].data[0], e.data[1].data[2].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 << "Fp12 small power res vs output\n"; + for(std::size_t i = 0; i < 12; 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 < 12; 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, + nil::blueprint::connectedness_check_type::type::STRONG); +} + +template +void test_fp12_frobenius_map(std::vector public_input) { + 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 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_frobenius_map; + + typename component_type::input_type instance_input; + typename std::array X; + typename std::array expected_res; + + for(std::size_t i = 0; i < 12; i++) { + instance_input.x[i] = var(0,i, false, var::column_type::public_input); + X[i] = public_input[i]; + } + + typename FieldType::integral_type field_p = FieldType::modulus; + using policy_type_fp12 = crypto3::algebra::fields::fp12_2over3over2; + using fp12_element = typename policy_type_fp12::value_type; + + fp12_element e1 = 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]} }), + e = e1; + + for(std::size_t i = 0; i < int(Power); i++) { + e = e.pow(field_p); // fp12 power raising + } + + expected_res = { + e.data[0].data[0].data[0], e.data[0].data[0].data[1], + e.data[0].data[1].data[0], e.data[0].data[1].data[1], + e.data[0].data[2].data[0], e.data[0].data[2].data[1], + e.data[1].data[0].data[0], e.data[1].data[0].data[1], + e.data[1].data[1].data[0], e.data[1].data[1].data[1], + e.data[1].data[2].data[0], e.data[1].data[2].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 << "Fp12 Frobenius map res vs output\n"; + for(std::size_t i = 0; i < 12; 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 < 12; 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, + nil::blueprint::connectedness_check_type::type::STRONG); +} + +template +void test_fp12_power_t(std::vector public_input) { + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = (WitnessColumns == 12)? 4 : 5; + + 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::fp12_power_t; + + typename component_type::input_type instance_input; + typename std::array X; + typename std::array expected_res; + + for(std::size_t i = 0; i < 12; i++) { + instance_input.x[i] = var(0,i, false, var::column_type::public_input); + X[i] = public_input[i]; + } + + using policy_type_fp12 = crypto3::algebra::fields::fp12_2over3over2; + using fp12_element = typename policy_type_fp12::value_type; + + fp12_element e1 = 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]} }), + e = e1.pow(0xD201000000010000); // fp12 power raising + + expected_res = { + e.data[0].data[0].data[0], e.data[0].data[0].data[1], + e.data[0].data[1].data[0], e.data[0].data[1].data[1], + e.data[0].data[2].data[0], e.data[0].data[2].data[1], + e.data[1].data[0].data[0], e.data[1].data[0].data[1], + e.data[1].data[1].data[0], e.data[1].data[1].data[1], + e.data[1].data[2].data[0], e.data[1].data[2].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 << "Fp12 power (-t) res vs output\n"; + for(std::size_t i = 0; i < 12; 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 < 12; 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, + nil::blueprint::connectedness_check_type::type::STRONG); +} + +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 << "\n"; + std::vector ab = {}, + x = {}; + + for(std::size_t j = 0; j < 2*12; j++) { + ab.push_back(generate_random()); + } + test_fp12_multiplication(ab); + test_fp12_multiplication(ab); + test_fp12_multiplication(ab); + test_fp12_multiplication(ab); + + for(std::size_t j = 0; j < 12; j++) { + x.push_back(generate_random()); + } + + test_fp12_inversion(x); + test_fp12_inversion(x); + test_fp12_inversion(x); + + test_fp12_small_power(x); + test_fp12_small_power(x); + test_fp12_small_power(x); + + test_fp12_frobenius_map(x); + test_fp12_frobenius_map(x); + test_fp12_frobenius_map(x); + + test_fp12_power_t(x); + test_fp12_power_t(x); + } + + std::vector x = {}; + x.resize(12,field_type::value_type::zero()); + // test to fail + test_fp12_inversion(x); +} + +BOOST_AUTO_TEST_SUITE_END()