From ebccfac3f7d11786c7e04793b740eee2d6c1381b Mon Sep 17 00:00:00 2001 From: Vasiliy Olekhov Date: Thu, 21 Mar 2024 17:07:45 +0200 Subject: [PATCH 1/7] Working MNT4 pairing components: Miller loop and Final exponentiation #355 --- cmake/modules | 2 +- .../plonk/non_native/detail/abstract_fp2.hpp | 76 ++ .../plonk/non_native/detail/abstract_fp3.hpp | 77 ++ .../plonk/non_native/detail/abstract_fp4.hpp | 87 ++ .../plonk/non_native/mnt4_fp4_fixed_power.hpp | 510 ++++++++++++ .../plonk/non_native/mnt6_fp6_fixed_power.hpp | 495 ++++++++++++ .../weierstrass/plonk/mnt4_exponentiation.hpp | 439 ++++++++++ .../weierstrass/plonk/mnt4_miller_loop.hpp | 751 ++++++++++++++++++ test/CMakeLists.txt | 3 + .../plonk/non_native/mnt4_fp4_fixed_power.cpp | 160 ++++ .../weierstrass/plonk/mnt4_exponentiation.cpp | 138 ++++ .../weierstrass/plonk/mnt4_pairing.cpp | 147 ++++ test/test_plonk_component.hpp | 21 +- 13 files changed, 2899 insertions(+), 7 deletions(-) create mode 100644 include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp2.hpp create mode 100644 include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp3.hpp create mode 100644 include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp4.hpp create mode 100644 include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp create mode 100644 include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp create mode 100644 include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp create mode 100644 include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.hpp create mode 100644 test/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.cpp create mode 100644 test/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.cpp create mode 100644 test/algebra/pairing/weierstrass/plonk/mnt4_pairing.cpp diff --git a/cmake/modules b/cmake/modules index a016a4216..5e6b354ea 160000 --- a/cmake/modules +++ b/cmake/modules @@ -1 +1 @@ -Subproject commit a016a42163a58ef68bbde48082d0ddafe68b8e56 +Subproject commit 5e6b354eaa69f6a275992e08220e925c34ba0a19 diff --git a/include/nil/blueprint/components/algebra/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..9c67ff754 --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp2.hpp @@ -0,0 +1,76 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Alexey Yashunsky +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of F_p^2 elements over an abstract entity (to be used with constraints) +// with F_p^2 = F_p[u]/(u^2 - non_residue). +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP2_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP2_HPP + +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + template + class abstract_fp2_element { + public: + using policy_type_fp2 = crypto3::algebra::fields::fp2; + 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[0] + policy_type_fp2::extension_policy::non_residue * data[1] * other[1], + data[0] * other[1] + data[1] * other[0]}; + } + constexpr abstract_fp2_element operator*(const int x) { + return { data[0]*x, data[1]*x }; + } + 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[0], data[1] + other[1] }; + } + constexpr abstract_fp2_element operator-(const abstract_fp2_element& other) { + return { data[0] - other[0], data[1] - other[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/detail/abstract_fp3.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp3.hpp new file mode 100644 index 000000000..2b191b914 --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp3.hpp @@ -0,0 +1,77 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of F_p^3 elements over an abstract entity (to be used with constraints) +// with F_p^3 = F_p[u]/(u^3 + non_residue). +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP3_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP3_HPP + +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + template + class abstract_fp3_element { + public: + std::array data; + + T& operator[](std::size_t idx) { + return data[idx]; + } + const T& operator[](std::size_t idx) const { + return data[idx]; + } + + constexpr abstract_fp3_element operator*(abstract_fp3_element const& other) { + auto s = UnderlyingFieldType::non_residue; + return { + data[0]*other[0] + s*(data[1]*other[2] + data[2]*other[1]), + data[0]*other[1] + data[1]*other[0] + s*data[2]*other[2], + data[0]*other[2] + data[1]*other[1] + data[2]*other[0] + }; + } + + constexpr abstract_fp3_element operator*(const int x) { + return { data[0]*x, data[1]*x, data[2]*x }; + } + friend abstract_fp3_element operator*(const int x, abstract_fp3_element const& e) { + return { e[0]*x, e[1]*x, e[2]*x }; + } + constexpr abstract_fp3_element operator+(abstract_fp3_element const& other) { + return { data[0] + other[0], data[1] + other[1], data[2] + other[2] }; + } + constexpr abstract_fp3_element operator-(abstract_fp3_element const& other) { + return { data[0] - other[0], data[1] - other[1], data[2] - other[2] }; + } + }; + + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP3_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp4.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp4.hpp new file mode 100644 index 000000000..b8602e65d --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp4.hpp @@ -0,0 +1,87 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of F_p^4 elements over an abstract entity (to be used with constraints) +// with F_p^4 = Fp^2 over Fp^2: +// Fp^4 = Fp^2[x]/(x^2 - u), u = (0,1) +// Fp^2 = Fp[y]/(y^2 - v), v = 17 (0x11), u^2 = v +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP4_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP4_HPP + +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + + template + class abstract_fp4_element{ + public: + using policy_type_fp4 = crypto3::algebra::fields::fp4; + std::array x; + + T& operator[](std::size_t idx) { + return x[idx]; + } + const T& operator[](std::size_t idx) const { + return x[idx]; + } + + constexpr abstract_fp4_element operator*(abstract_fp4_element const& y) { + // Devegili et al - Multiplication and squaring in pairing-friendly fields + // https://eprint.iacr.org/2006/471.pdf + constexpr auto s = policy_type_fp4::extension_policy::non_residue; + auto d00 = x[0b00]*y[0b00] + s*(x[0b01]*y[0b01] + x[0b10]*y[0b11] + x[0b11]*y[0b10]); + auto d01 = x[0b00]*y[0b01] + x[0b10]*y[0b10] + x[0b01]*y[0b00] + s*x[0b11]*y[0b11]; + auto d10 = x[0b00]*y[0b10] + x[0b10]*y[0b00] + s*(x[0b01]*y[0b11] + x[0b11]*y[0b01]); + auto d11 = x[0b00]*y[0b11] + x[0b01]*y[0b10] + x[0b10]*y[0b01] + x[0b11]*y[0b00]; + + return { d00, d01, d10, d11}; + } + + constexpr abstract_fp4_element operator*(const int a) { + return { x[0]*a, x[1]*a, x[2]*a, x[3]*a }; + } + friend abstract_fp4_element operator*(const int a, abstract_fp4_element const& x) { + return { x[0]*a, x[1]*a, x[2]*a, x[3]*a }; + } + constexpr abstract_fp4_element operator+(abstract_fp4_element const& y) { + return { x[0] + y[0], x[1] + y[1], x[2] + y[2], x[3] + y[3] }; + } + constexpr abstract_fp4_element operator-(abstract_fp4_element const& y) { + return { x[0] - y[0], x[1] - y[1], x[2] - y[2], x[3] - y[3] }; + } + }; + + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_abstract_FP4_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp new file mode 100644 index 000000000..ba59aacb3 --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp @@ -0,0 +1,510 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for F_p^{4} raising to a fixed power +// t, which is a parameter of the component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_FP4_FIXED_POWER_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_FP4_FIXED_POWER_HPP + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + + // + // Component for raising to a fixed power t in F_p^4 + // Input: x[4] + // Output: y[4]: y = x^t as elements of F_p^4 + // + + template + class mnt4_fp4_fixed_power; + + template + class mnt4_fp4_fixed_power, BlueprintFieldType> + : public plonk_component + { + public: + using component_type = plonk_component; + using var = typename component_type::var; + + using constraint_type = crypto3::zk::snark::plonk_constraint; + using policy_type = crypto3::algebra::fields::fp4; + using integral_type = typename BlueprintFieldType::integral_type; + using extended_integral_type = typename BlueprintFieldType::extended_integral_type; + using fp4_element = typename policy_type::value_type; + using fp4_constraint = detail::mnt4_fp4_element; + + private: + static std::vector base4(extended_integral_type x) { + if (x > 0) { + std::vector res = {std::uint8_t(x % 4)}; + x /= 4; + while (x > 0) { + res.insert(res.begin(), std::uint8_t(x % 4)); + x /= 4; + } + return res; + } else { + return {0}; + } + } + + static std::size_t gates_amount_internal(extended_integral_type power) { + std::size_t gates = 1; // at least one for power-4 operations + std::vector exp_plan = base4(power); + if (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1) { + gates++; // a multiplication gate + } + gates += (std::count(exp_plan.begin(),exp_plan.end(),3) > 0); // a cubing gate + gates += (std::count(exp_plan.begin(),exp_plan.end(),2) > 0); // a squaring gate + + return gates; + } + + public: + using manifest_type = plonk_component_manifest; + + const extended_integral_type power/* = pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0*/; + + const std::vector exp_plan, exp_precompute; + const std::size_t rows_amount; + + class gate_manifest_type : public component_gate_manifest { + const extended_integral_type power; + std::array gates_footprint(extended_integral_type power) const { + std::vector exp_plan = base4(power); + return { (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1), + (std::count(exp_plan.begin(),exp_plan.end(),3) > 0), + (std::count(exp_plan.begin(),exp_plan.end(),2) > 0) }; + } + public: + gate_manifest_type(extended_integral_type power) : power(power) {} + + std::uint32_t gates_amount() const override { + return mnt4_fp4_fixed_power::gates_amount_internal(power); + } + + bool operator<(const component_gate_manifest *other) const override { + extended_integral_type o_power = dynamic_cast(other)->power; + + std::array + gates = gates_footprint(power), + o_gates = gates_footprint(o_power); + return (gates < o_gates); + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount, + std::size_t lookup_column_amount, + extended_integral_type power) + { + static gate_manifest manifest = gate_manifest(gate_manifest_type(power)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_range_param(4, 300, 1)), + false + ); + return manifest; + } + + static std::vector get_precomputed_exps(const std::vector exps) + { + std::vector precompute = {exps[0]}; + if ((exps[0] != 3) && (std::count(exps.begin(),exps.end(),3) > 0)) { + precompute.insert(precompute.begin(),3); + } + if ((exps[0] != 2) && (std::count(exps.begin(),exps.end(),2) > 0)) { + precompute.insert(precompute.begin(),2); + } + return precompute; + } + + static std::size_t get_rows_amount(extended_integral_type power) + { + std::vector + exp_plan = base4(power), + exp_precompute = get_precomputed_exps(exp_plan); + + std::size_t rows = 0; + + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + rows += 1 + (exp_precompute[i] > 1); + } + for(std::size_t i = 1; i < exp_plan.size(); i++) { + rows += 1 + 2*(exp_plan[i] > 0); + } + return rows; + } + + struct input_type { + std::array x; + + std::vector> all_vars() { + return {x[0], x[1], x[2], x[3]}; + } + }; + + struct result_type { + std::array output; + + result_type(mnt4_fp4_fixed_power const& component, std::uint32_t start_row_index) + { + std::size_t last_row = start_row_index + component.rows_amount - 1; + + for(std::size_t i = 0; i < output.size(); i++) { + output[i] = var(component.W(i), last_row, false, var::column_type::witness); + } + } + + std::vector> all_vars() + { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template explicit + mnt4_fp4_fixed_power( + ContainerType witness, + extended_integral_type power) : + component_type(witness, {}, {}, get_manifest()), + power(power), + exp_plan(base4(power)), + exp_precompute(get_precomputed_exps(exp_plan)), + rows_amount(get_rows_amount(power)) + { }; + + template + mnt4_fp4_fixed_power( + WitnessContainerType witness, + ConstantContainerType constant, + PublicInputContainerType public_input, + extended_integral_type power) : + component_type(witness, constant, public_input, get_manifest()), + power(power), + exp_plan(base4(power)), + exp_precompute(get_precomputed_exps(exp_plan)), + rows_amount(get_rows_amount(power)) + { }; + + mnt4_fp4_fixed_power( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list public_inputs, + extended_integral_type power) : + component_type(witnesses, constants, public_inputs, get_manifest()), + power(power), + exp_plan(base4(power)), + exp_precompute(get_precomputed_exps(exp_plan)), + rows_amount(get_rows_amount(power)) + { }; + }; + + /* */ + + template + using component_type = mnt4_fp4_fixed_power, BlueprintFieldType>; + + template + typename component_type::result_type + generate_assignments( + component_type const& component, + assignment> & assignment, + typename component_type::input_type const& instance_input, + const std::uint32_t start_row_index) + { + + using value_type = typename BlueprintFieldType::value_type; + using policy_type = typename component_type::policy_type; + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + std::array::policy_type::arity> x; + + for(std::size_t i = 0; i < x.size(); i++) { + x[i] = var_value(assignment, instance_input.x[i]); + } + + using fp4_element = typename component_type::fp4_element; + fp4_element X = fp4_element({ {x[0],x[1]}, {x[2],x[3]},}), Y = X; + + std::size_t row = 0; + + auto fill_row = [&component, &assignment, &start_row_index, &row](fp4_element const& V) { + value_type d00 = V.data[0].data[0]; + value_type d01 = V.data[0].data[1]; + value_type d10 = V.data[1].data[0]; + value_type d11 = V.data[1].data[1]; + assignment.witness(component.W(0),start_row_index + row) = d00; + assignment.witness(component.W(1),start_row_index + row) = d01; + assignment.witness(component.W(2),start_row_index + row) = d10; + assignment.witness(component.W(3),start_row_index + row) = d11; + + /* + for(std::size_t i = 0; i < policy_type::arity; i++) { + assignment.witness(component.W(i),start_row_index + row) = V.data[i/2].data[i % 2]; + } + */ + row++; + }; + + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + fill_row(X); // X + if (exp_precompute[i] > 1) { + fill_row(X.pow(exp_precompute[i])); + } + } + + Y = X.pow(exp_plan[0]); + for(std::size_t i = 1; i < exp_plan.size(); i++) { + Y = Y.pow(4); fill_row(Y); // every step includes a power-4 operation + if (exp_plan[i] > 0) { // for every non-zero digit we need a multiplication too + fill_row(X.pow(exp_plan[i])); + Y = Y * X.pow(exp_plan[i]); fill_row(Y); + } + } + + return typename component_type::result_type( + component, start_row_index); + } + + template + std::vector generate_gates( + component_type const& component, + circuit> & bp, + assignment> const& assignment, + typename component_type::input_type const& instance_input) + { + using var = typename component_type::var; + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + std::vector gate_list = {}; // at most 4 gate ids + + + using policy_type = typename component_type::policy_type; + typename component_type::fp4_constraint X, Y, Z, C; + + // power-4 gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = (X * X) * (X * X); + + using constraint_type = typename component_type::constraint_type; + + std::vector pow4_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + pow4_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(pow4_constrs)); + + if (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1) { + // at least one digit besides the first is non-zero + // => we need a multiplication gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + Z[i] = var(component.W(i), 1, true); + } + C = X * Y; + + std::vector mult_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + mult_constrs.push_back(C[i] - Z[i]); + } + gate_list.push_back(bp.add_gate(mult_constrs)); + } + + if (std::count(exp_precompute.begin(),exp_precompute.end(),3)) { + // we need a cubing gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = X * X * X; + + std::vector cube_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + cube_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(cube_constrs)); + } + + if (std::count(exp_precompute.begin(),exp_precompute.end(),2)) { + // we need a squaring gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = X * X; + + std::vector square_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + square_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(square_constrs)); + } + + return gate_list; + } + + template + void generate_copy_constraints( + component_type const& component, + circuit> &bp, + assignment> &assignment, + const typename component_type::input_type &instance_input, + const std::size_t start_row_index) + { + using var = typename component_type::var; + using policy_type = typename component_type::policy_type; + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + // for storing relative ids of rows where x^0, x^1, x^2 and x^3 are stored + std::array row_of_power = {0,1,0,0}; + + for(std::size_t small_power = 3; small_power > 0; small_power--) { + if (std::count(exp_precompute.begin(),exp_precompute.end(),small_power)) { + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + if (exp_precompute[i] == small_power) { + // this gives a wrong value for small_power = 1, but it is coherent with the next part + row_of_power[small_power] = 2*i + 1; + } + } + } + } + + // copies of initial data + for(std::size_t j = 1; j < 4; j++) { + if (row_of_power[j] > 0) { // => we need a copy of initial data before row_of_power[j] + for(std::size_t i = 0; i < policy_type::arity; i++) { + bp.add_copy_constraint({var(component.W(i), start_row_index + row_of_power[j]-1, false), instance_input.x[i]}); + } + } + } + row_of_power[1] = 0; // from now on we need the real row number for where x^1 is stored + + std::size_t row = 0; + for(std::size_t j = 0; j < exp_precompute.size(); j++) { + row += 1 + (exp_precompute[j] > 1); // for x² and x³ skip 2 rows, for x just one + } + for(std::size_t j = 1; j < exp_plan.size(); j++) { + row++; // skip the power-4 value + if (exp_plan[j] > 0) { // this row has a copy of some precomputed row + for(std::size_t i = 0; i < policy_type::arity; i++) { + bp.add_copy_constraint({ + var(component.W(i), start_row_index + row_of_power[exp_plan[j]], false), + var(component.W(i), start_row_index + row, false) + }); + } + row += 2; + } + } + } + + template + typename component_type::result_type + generate_circuit( + component_type const& component, + circuit>& bp, + assignment>& assignment, + typename component_type::input_type const& instance_input, + const std::size_t start_row_index) + { + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + std::size_t row = 0; + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + row += 1 + (exp_precompute[i] > 1); // for x² and x³ skip 2 rows, for x just one + } + for(std::size_t i = 1; i < exp_plan.size(); i++) { + assignment.enable_selector(selector_index[0], start_row_index + row); // power-4 gate + row++; + if (exp_plan[i] > 0) { + assignment.enable_selector(selector_index[1], start_row_index + row); // multiplication gate + row += 2; + } + } + // did we even use a multiplication gate? + std::size_t gate_id = 1 + (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1); + + // cubing and squaring gates if we need either of them + for(std::size_t small_power = 3; small_power > 1; small_power--) { + if (std::count(exp_precompute.begin(),exp_precompute.end(),small_power)) { + std::size_t row_of_power; + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + if (exp_precompute[i] == small_power) { + row_of_power = 2*i + 1; + } + } + assignment.enable_selector(selector_index[gate_id], start_row_index + row_of_power); + gate_id++; + } + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename component_type::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_FP4_FIXED_POWER_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp new file mode 100644 index 000000000..1ede12082 --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp @@ -0,0 +1,495 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2023 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for F_p^{6} raising to a fixed power +// t, which is a parameter of the component. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_FP6_FIXED_POWER_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_FP6_FIXED_POWER_HPP + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + + // + // Component for raising to a fixed power t in F_p^6 (fp6_2over3) + // Input: x[6] + // Output: y[6]: y = x^t as elements of F_p^6 + // + + template + class mnt6_fp6_fixed_power; + + template + class mnt6_fp6_fixed_power> + : public plonk_component + { + public: + using component_type = plonk_component; + using var = typename component_type::var; + + using constraint_type = crypto3::zk::snark::plonk_constraint; + using policy_type = crypto3::algebra::fields::fp6_2over3; + using integral_type = typename policy_type::extension_policy::integral_type; + using fp6_element = typename policy_type::value_type; + using fp6_constraint = detail::mnt6_fp6_element; + + private: + static std::vector base4(integral_type x) { + if (x > 0) { + std::vector res = {std::uint8_t(x % 4)}; + x /= 4; + while (x > 0) { + res.insert(res.begin(), std::uint8_t(x % 4)); + x /= 4; + } + return res; + } else { + return {0}; + } + } + + static std::size_t gates_amount_internal(integral_type power) { + std::size_t gates = 1; // at least one for power-4 operations + std::vector exp_plan = base4(power); + if (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1) { + gates++; // a multiplication gate + } + gates += (std::count(exp_plan.begin(),exp_plan.end(),3) > 0); // a cubing gate + gates += (std::count(exp_plan.begin(),exp_plan.end(),2) > 0); // a squaring gate + + return gates; + } + + public: + using manifest_type = plonk_component_manifest; + + const integral_type power/* = pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0*/; + + const std::vector exp_plan, exp_precompute; + const std::size_t rows_amount; + + class gate_manifest_type : public component_gate_manifest { + std::array gates_footprint(integral_type power) const { + std::vector exp_plan = base4(power); + return { (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1), + (std::count(exp_plan.begin(),exp_plan.end(),3) > 0), + (std::count(exp_plan.begin(),exp_plan.end(),2) > 0) }; + } + public: + const integral_type power; + gate_manifest_type(integral_type power) : power(power) {} + + std::uint32_t gates_amount() const override { + return mnt6_fp6_fixed_power::gates_amount_internal(power); + } + + bool operator<(const component_gate_manifest *other) const override { + integral_type o_power = dynamic_cast(other)->power; + + std::array + gates = gates_footprint(power), + o_gates = gates_footprint(o_power); + return (gates < o_gates); + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount, + std::size_t lookup_column_amount, + integral_type power) + { + static gate_manifest manifest = gate_manifest(gate_manifest_type(power)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(policy_type::arity)), // ?? + false + ); + return manifest; + } + + static std::vector get_precomputed_exps(const std::vector exps) + { + std::vector precompute = {exps[0]}; + if ((exps[0] != 3) && (std::count(exps.begin(),exps.end(),3) > 0)) { + precompute.insert(precompute.begin(),3); + } + if ((exps[0] != 2) && (std::count(exps.begin(),exps.end(),2) > 0)) { + precompute.insert(precompute.begin(),2); + } + return precompute; + } + + static std::size_t get_rows_amount( + std::size_t witness_amount, + std::size_t lookup_column_amount, + integral_type power) + { + std::vector + exp_plan = base4(power), + exp_precompute = get_precomputed_exps(exp_plan); + + std::size_t rows = 0; + + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + rows += 1 + (exp_precompute[i] > 1); + } + for(std::size_t i = 1; i < exp_plan.size(); i++) { + rows += 1 + 2*(exp_plan[i] > 0); + } + return rows; + } + + struct input_type { + std::array x; + + std::vector> all_vars() { + return {x[0], x[1], x[2], x[3], x[4], x[5]}; + } + }; + + struct result_type { + std::array output; + + result_type(mnt6_fp6_fixed_power const& component, std::uint32_t start_row_index) + { + std::size_t last_row = start_row_index + component.rows_amount - 1; + + for(std::size_t i = 0; i < output.size(); i++) { + output[i] = var(component.W(i), last_row, false, var::column_type::witness); + } + } + + std::vector> all_vars() + { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template explicit + mnt6_fp6_fixed_power( + ContainerType witness, + integral_type power) : + component_type(witness, {}, {}, get_manifest()), + power(power), + exp_plan(base4(power)), + exp_precompute(get_precomputed_exps(exp_plan)), + rows_amount(get_rows_amount(this->witness_amount(), 0, power)) + { }; + + template + mnt6_fp6_fixed_power( + WitnessContainerType witness, + ConstantContainerType constant, + PublicInputContainerType public_input, + integral_type power) : + component_type(witness, constant, public_input, get_manifest()), + power(power), + exp_plan(base4(power)), + exp_precompute(get_precomputed_exps(exp_plan)), + rows_amount(get_rows_amount(this->witness_amount(), 0, power)) + { }; + + mnt6_fp6_fixed_power( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list public_inputs, + integral_type power) : + component_type(witnesses, constants, public_inputs, get_manifest()), + power(power), + exp_plan(base4(power)), + exp_precompute(get_precomputed_exps(exp_plan)), + rows_amount(get_rows_amount(this->witness_amount(), 0, power)) + { }; + }; + + /* */ + + template + typename mnt6_fp6_fixed_power::result_type + generate_assignments( + mnt6_fp6_fixed_power const& component, + assignment> const& assignment, + typename mnt6_fp6_fixed_power::input_type const& instance_input, + const std::uint32_t start_row_index) + { + + using value_type = typename BlueprintFieldType::value_type; + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + std::array::policy_type::arity> x; + + for(std::size_t i = 0; i < x.size(); i++) { + x[i] = var_value(assignment, instance_input.x[i]); + } + + using fp6_element = typename mnt6_fp6_fixed_power::fp6_element; + fp6_element X = fp6_element({ {x[0],x[1], x[2]}, {x[3],x[4]},x[5]}), Y = X; + + std::size_t row = 0; + + auto fill_row = [&component, &assignment, &start_row_index, &row](fp6_element V) { + for(std::size_t i = 0; i < V.arity; i++) { + assignment.witness(component.W(i),start_row_index + row) = V.data[i/3].data[i % 3]; + } + row++; + }; + + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + fill_row(X); // X + if (exp_precompute[i] > 1) { + fill_row(X.pow(exp_precompute[i])); + } + } + + Y = X.pow(exp_plan[0]); + for(std::size_t i = 1; i < exp_plan.size(); i++) { + Y = Y.pow(4); fill_row(Y); // every step includes a power-4 operation + if (exp_plan[i] > 0) { // for every non-zero digit we need a multiplication too + fill_row(X.pow(exp_plan[i])); + Y = Y * X.pow(exp_plan[i]); fill_row(Y); + } + } + + return typename mnt6_fp6_fixed_power::result_type( + component, start_row_index); + } + + template + std::vector generate_gates( + mnt6_fp6_fixed_power const& component, + circuit> & bp, + assignment> const& assignment, + typename mnt6_fp6_fixed_power::input_type const& instance_input) + { + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + std::vector gate_list = {}; // at most 4 gate ids + + + using policy_type = typename mnt6_fp6_fixed_power::policy_type; + typename mnt6_fp6_fixed_power::fp4_constraint X, Y, Z, C; + + // power-4 gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = (X * X) * (X * X); + + using constraint_type = typename mnt6_fp6_fixed_power::constraint_type; + + std::vector pow4_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + pow4_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(pow4_constrs)); + + if (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1) { + // at least one digit besides the first is non-zero + // => we need a multiplication gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + Z[i] = var(component.W(i), 1, true); + } + C = X * Y; + + std::vector mult_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + mult_constrs.push_back(C[i] - Z[i]); + } + gate_list.push_back(bp.add_gate(mult_constrs)); + } + + if (std::count(exp_precompute.begin(),exp_precompute.end(),3)) { + // we need a cubing gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = X * X * X; + + std::vector cube_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + cube_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(cube_constrs)); + } + + if (std::count(exp_precompute.begin(),exp_precompute.end(),2)) { + // we need a squaring gate + for(std::size_t i = 0; i < policy_type::arity; i++) { + X[i] = var(component.W(i), -1, true); + Y[i] = var(component.W(i), 0, true); + } + C = X * X; + + std::vector square_constrs = {}; + for(std::size_t i = 0; i < policy_type::arity; i++) { + square_constrs.push_back(C[i] - Y[i]); + } + gate_list.push_back(bp.add_gate(square_constrs)); + } + return gate_list; + } + + template + void generate_copy_constraints( + mnt6_fp6_fixed_power const& component, + circuit> &bp, + assignment> &assignment, + const typename mnt6_fp6_fixed_power::input_type &instance_input, + const std::size_t start_row_index) + { + using var = typename mnt6_fp6_fixed_power::var; + using policy_type = typename mnt6_fp6_fixed_power::policy_type; + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + // for storing relative ids of rows where x^0, x^1, x^2 and x^3 are stored + std::array row_of_power = {0,1,0,0}; + + for(std::size_t small_power = 3; small_power > 0; small_power--) { + if (std::count(exp_precompute.begin(),exp_precompute.end(),small_power)) { + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + if (exp_precompute[i] == small_power) { + // this gives a wrong value for small_power = 1, but it is coherent with the next part + row_of_power[small_power] = 2*i + 1; + } + } + } + } + + // copies of initial data + for(std::size_t j = 1; j < 4; j++) { + if (row_of_power[j] > 0) { // => we need a copy of initial data before row_of_power[j] + for(std::size_t i = 0; i < policy_type::arity; i++) { + bp.add_copy_constraint({var(component.W(i), start_row_index + row_of_power[j]-1, false), instance_input.x[i]}); + } + } + } + row_of_power[1] = 0; // from now on we need the real row number for where x^1 is stored + + std::size_t row = 0; + for(std::size_t j = 0; j < exp_precompute.size(); j++) { + row += 1 + (exp_precompute[j] > 1); // for x² and x³ skip 2 rows, for x just one + } + for(std::size_t j = 1; j < exp_plan.size(); j++) { + row++; // skip the power-4 value + if (exp_plan[j] > 0) { // this row has a copy of some precomputed row + for(std::size_t i = 0; i < policy_type::arity; i++) { + bp.add_copy_constraint({ + var(component.W(i), start_row_index + row_of_power[exp_plan[j]], false), + var(component.W(i), start_row_index + row, false) + }); + } + row += 2; + } + } + } + + template + typename mnt6_fp6_fixed_power::result_type + generate_circuit( + mnt6_fp6_fixed_power const& component, + circuit>& bp, + assignment>& assignment, + typename mnt6_fp6_fixed_power::input_type const& instance_input, + const std::size_t start_row_index) + { + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + + const std::vector + exp_plan = component.exp_plan, + exp_precompute = component.exp_precompute; + + std::size_t row = 0; + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + row += 1 + (exp_precompute[i] > 1); // for x² and x³ skip 2 rows, for x just one + } + for(std::size_t i = 1; i < exp_plan.size(); i++) { + assignment.enable_selector(selector_index[0], start_row_index + row); // power-4 gate + row++; + if (exp_plan[i] > 0) { + assignment.enable_selector(selector_index[1], start_row_index + row); // multiplication gate + row += 2; + } + } + // did we even use a multiplication gate? + std::size_t gate_id = 1 + (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1); + + // cubing and squaring gates if we need either of them + for(std::size_t small_power = 3; small_power > 1; small_power--) { + if (std::count(exp_precompute.begin(),exp_precompute.end(),small_power)) { + std::size_t row_of_power; + for(std::size_t i = 0; i < exp_precompute.size(); i++) { + if (exp_precompute[i] == small_power) { + row_of_power = 2*i + 1; + } + } + assignment.enable_selector(selector_index[gate_id], start_row_index + row_of_power); + gate_id++; + } + } + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename mnt6_fp6_fixed_power::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_FP6_FIXED_POWER_HPP diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp new file mode 100644 index 000000000..12336f795 --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp @@ -0,0 +1,439 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Circuit for final exponentiation for MNT4 elliptic curve pairings +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_EXPONENTIATION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_EXPONENTIATION_HPP + +#include "nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp" +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + // + // Curve E over prime field (F_p) as q points + // + // Pairing is possible due to q | p^k-1 + // + // This component raises element in F_{p^k} to power F = (p^k-1)/q + // + // For MNT4 curve k = 4 and F = (p^2-1)(p+w_0) + // + // The process of raising x to power F takes 6 stages: + // 1. Raise x to power p, x <- x^p (on Fp4 this is cheap) + // 2. Repeat: x <- x^p, now x holds x^(p^2) + // 3. Divide by initial value x, now x holds x^(p^2-1), save to x' + // 4. Raise x' to power p: x1 <- x'^p (cheap) + // 5. Raise x' to power w0: x2 <- x'^w0 (this is hard) + // 6. Result is x1*x2 + // + // Circuit requires 4 witnesses, 4 inputs and 4 outputs + // 6 gates are used: + // Gate 0: Raising to power p, "Frobenius map" + // Gate 1: "Division in Fp4" + // Gate 2: "Multiplication" + // Gate 3: "Squaring" + // Gate 4: "Cubing" + // Gate 5: "Fourth power" + // Gates 3-5 are used for powering to w0 + using namespace detail; + + template + class mnt4_exponentiation; + + template + class mnt4_exponentiation> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + using curve_type = nil::crypto3::algebra::curves::mnt4<298>; + + using fixed_power_type = mnt4_fp4_fixed_power, BlueprintFieldType>; + + class gate_manifest_type : public component_gate_manifest { + public: + gate_manifest_type() {} + + std::uint32_t gates_amount() const override { + return mnt4_exponentiation::gates_amount; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount) + { + static gate_manifest manifest = gate_manifest(gate_manifest_type()); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(4)), + false + ); + + return manifest; + } + + constexpr static std::size_t get_rows_amount( + std::size_t witness_amount) + { + return fixed_power_type::get_rows_amount(crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0) + 9 - 1; + } + + constexpr static const std::size_t gates_amount = 7; + const std::size_t rows_amount = get_rows_amount(0); + + struct input_type { + std::array x; + + std::vector> all_vars() { + return {x[0], x[1], x[2], x[3]}; + } + }; + + struct result_type { + std::array output; + + result_type(const mnt4_exponentiation &component, std::uint32_t start_row_index) { + std::size_t last_row = start_row_index + component.rows_amount - 1; + for(std::size_t i = 0; i < 4; i++) { + output[i] = var(component.W(i), last_row, false, var::column_type::witness); + } + } + + std::vector> all_vars() { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template + explicit mnt4_exponentiation(ContainerType witness) : + component_type(witness, {}, {}, get_manifest()) {}; + + template + mnt4_exponentiation( + WitnessContainerType witness, + ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + mnt4_exponentiation( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, unsigned long long T_) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_mnt4_exponentiation = + mnt4_exponentiation>; + + template + typename plonk_mnt4_exponentiation::result_type + generate_assignments( + plonk_mnt4_exponentiation const& component, + assignment> &assignment, + typename plonk_mnt4_exponentiation::input_type const& instance_input, + const std::uint32_t start_row_index) + { + using component_type = plonk_mnt4_exponentiation; + using var = typename component_type::var; + using value_type = typename BlueprintFieldType::value_type; + using curve_type = nil::crypto3::algebra::curves::mnt4<298>; + + using fixed_power_type = mnt4_fp4_fixed_power, BlueprintFieldType>; + + fixed_power_type fixed_power_instance(component._W, component._C, component._PI, crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0); + + std::array x; + + for(std::size_t i = 0; i < 4; i++) { + x[i] = var_value(assignment, instance_input.x[i]); + } + + using policy_type_fp4 = crypto3::algebra::fields::fp4; + using fp4_element = typename policy_type_fp4::value_type; + + fp4_element + input = fp4_element({ {x[0],x[1]}, {x[2],x[3]}, }), + elt = input; + + std::size_t row = start_row_index; + + auto fill_row = [&assignment, &component, &row](fp4_element const& V) + { + for(std::size_t i = 0; i < 4; ++i) { + assignment.witness(component.W(i),row) = V.data[i/2].data[i%2]; + } + ++row; + }; + + // Initial value + fill_row(elt); + + // elt <- elt^p + elt = elt.Frobenius_map(1); + fill_row(elt); + + // elt <- (elt^p), now elt holds= x^(p^2) + elt = elt.Frobenius_map(1); + fill_row(elt); + + // elt <- elt/x, elt now holds x^(p^2-1) + fill_row(input); + elt = elt*input.inversed(); + fill_row(elt); + + // elt2 <- elt^p, elt2 = x^(p^2-1)*p + fp4_element elt2 = elt.Frobenius_map(1); + fill_row(elt2); + + /* Fill rows for raising elt = x^(p^2-1) to power w0 */ + + // The input is from 4th row + std::array transfer_vars = { + var(component.W(0), start_row_index + 4, false), + var(component.W(1), start_row_index + 4, false), + var(component.W(2), start_row_index + 4, false), + var(component.W(3), start_row_index + 4, false), + }; + + typename fixed_power_type::input_type pow_input = { transfer_vars }; + typename fixed_power_type::result_type pow_output = + generate_assignments(fixed_power_instance, assignment, pow_input, row); + row += fixed_power_instance.rows_amount; + + fp4_element elt3({ + { + var_value(assignment, pow_output.output[0]), + var_value(assignment, pow_output.output[1]), + } , { + var_value(assignment, pow_output.output[2]), + var_value(assignment, pow_output.output[3]) + } + }); + // Now elt3 holds x^(p^2-1)*w0 + // fill_row(elt3); + + // Final result is elt2*elt3 = x^((p^2-1)*p) * x^((p^2-1)*w0) = x^(p^2-1)*(p+w0) + fill_row(elt2); + elt = elt2*elt3; + fill_row(elt); + + return typename plonk_mnt4_exponentiation::result_type( + component, start_row_index); + } + + template + std::vector + generate_gates( + plonk_mnt4_exponentiation const& component, + circuit> &bp, + assignment> &assignment, + typename plonk_mnt4_exponentiation::input_type const& instance_input) + { + using var = typename plonk_mnt4_exponentiation::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using fp4_constraint = detail::abstract_fp4_element; + + std::vector gate_list = {}; + + std::vector constrs = {}; + + fp4_constraint X, Xn, Xp, R; + + for(std::size_t i = 0; i < 4; ++i) { + X[i] = var(component.W(i), 0, true); + Xn[i] = var(component.W(i), 1, true); + Xp[i] = var(component.W(i), -1, true); + } + + /* Frobenius gate - 0 + * Ensures x_next = x^p = x.frobenius_map(1) + */ + { + using fp4_ep = typename crypto3::algebra::fields::fp4::extension_policy; + using fp2_ep = typename crypto3::algebra::fields::fp2::extension_policy; + + constrs.clear(); + for(std::size_t i = 0; i < 4; ++i) { + typename BlueprintFieldType::value_type + fc4 = fp4_ep::Frobenius_coeffs_c1[i/2], + fc2 = fp2_ep::Frobenius_coeffs_c1[i%2]; + constraint_type coeff = constraint_type() + fc4*fc2; + constrs.push_back(Xn[i] - coeff*X[i]); + } + + gate_list.push_back(bp.add_gate(constrs)); + } + + /* Division gate - 1 + * Ensures x_next = x_prev/x : x_next * x - x_prev = 0 + */ + { + R = Xn*X - Xp; + + constrs.clear(); + constrs.push_back(R[0]); + constrs.push_back(R[1]); + constrs.push_back(R[2]); + constrs.push_back(R[3]); + + gate_list.push_back(bp.add_gate(constrs)); + } + + /* Multiplication gate - 2 + * Ensures x_next = x*x_prev: x_next - x*x_prev = 0 + */ + { + R = Xn - X*Xp; + + constrs.clear(); + constrs.push_back(R[0]); + constrs.push_back(R[1]); + constrs.push_back(R[2]); + constrs.push_back(R[3]); + + gate_list.push_back(bp.add_gate(constrs)); + } + + return gate_list; + } + + template + void generate_copy_constraints( + plonk_mnt4_exponentiation const& component, + circuit> &bp, + assignment> &assignment, + const typename plonk_mnt4_exponentiation::input_type &instance_input, + const std::size_t start_row_index) + { + using component_type = plonk_mnt4_exponentiation; + using var = typename plonk_mnt4_exponentiation::var; + + using fixed_power_type = typename component_type::fixed_power_type; + using curve_type = nil::crypto3::algebra::curves::mnt4<298>; + fixed_power_type power_instance( component._W, component._C, component._PI, + crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0); + std::size_t R = power_instance.rows_amount; + + // initial data in row 0 + for(std::size_t i = 0; i < 4; ++i) { + bp.add_copy_constraint({var(component.W(i), start_row_index, false), instance_input.x[i]}); + } + + // initial data in row 3 + for(std::size_t i = 0; i < 4; ++i) { + bp.add_copy_constraint({var(component.W(i), start_row_index + 3, false), instance_input.x[i]}); + } + + // Copy from 5 row to R+6 row + for(std::size_t i = 0; i < 4; ++i) { + bp.add_copy_constraint({ + var(component.W(i), start_row_index + R + 6, false), + var(component.W(i), start_row_index + 5, false), + }); + } + } + + template + typename plonk_mnt4_exponentiation::result_type + generate_circuit( + plonk_mnt4_exponentiation const& component, + circuit> &bp, + assignment> &assignment, + const typename plonk_mnt4_exponentiation::input_type &instance_input, + const std::size_t start_row_index) + { + using component_type = plonk_mnt4_exponentiation; + using var = typename component_type::var; + using fixed_power_type = typename component_type::fixed_power_type; + using curve_type = nil::crypto3::algebra::curves::mnt4<298>; + + fixed_power_type power_instance( component._W, component._C, component._PI, + crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0); + std::size_t R = power_instance.rows_amount; + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + + // Frobenius gates + assignment.enable_selector(selector_index[0], start_row_index + 0); + assignment.enable_selector(selector_index[0], start_row_index + 1); + assignment.enable_selector(selector_index[0], start_row_index + 4); + + // Division gate + assignment.enable_selector(selector_index[1], start_row_index + 3); + + // Power to w0 sub-circuit takes input from 4th rouw + std::array power_input_vars; + power_input_vars[0] = var(component.W(0), start_row_index + 4, false); + power_input_vars[1] = var(component.W(1), start_row_index + 4, false); + power_input_vars[2] = var(component.W(2), start_row_index + 4, false); + power_input_vars[3] = var(component.W(3), start_row_index + 4, false); + + typename fixed_power_type::input_type power_input = { power_input_vars }; + typename fixed_power_type::result_type power_output = + generate_circuit(power_instance, bp, assignment, power_input, start_row_index + 6); + + // expect result at start_rows_index + 6 + R + + // Multiplication gate + assignment.enable_selector(selector_index[2], start_row_index + R + 6); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_mnt4_exponentiation::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_mnt4_exponentiation_HPP diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.hpp new file mode 100644 index 000000000..0d4627367 --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.hpp @@ -0,0 +1,751 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of Miller loop component for MNT4 pairings +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_MILLER_LOOP_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_MILLER_LOOP_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + template + std::vector base(T x) { + std::vector res = {(std::uint8_t)(x % B)}; + if (x > 0) { + x /= B; + while (x > 0) { + res.insert(res.begin(), std::uint8_t(x % B)); + x /= B; + } + } + return res; + } + } // namespace detail + // + using mnt4_g2_params = crypto3::algebra::curves::detail:: + mnt4_g2_params<298,crypto3::algebra::curves::forms::short_weierstrass>; + + using mnt4_pairing_params = crypto3::algebra::pairing::detail:: + pairing_params; + + // + // Component for computing the result of applying the Miller loop for MNT4 curve + // to two points P from E(F_p) and Q from E'(F_p^2). + // Input: P[2], Q[4] ( we assume P and Q are NOT (0,0), i.e. not the points at infinity, NOT CHECKED ) + // Output: f[4]: an element of F_p^4 + // + // Each iteration of the Miller loop adds "doubling" row to the circuit: + // + // f0 f1 f2 f3 P0 P1 T0 T1 T2 T3 Q0 Q1 Q2 Q3 L0 L1 L2 L3 + // Gate 0: "doubling" + // Constraints: + // 0. UT = untwisted T + // 1. L = (3*UT.x^2+a)/(2*UT.y) + // 2. f_next = f*f*(L*(P.x-UT.x) - (P.y - UT.y)) + // 3. T_next = T + T + // + // If current iteration needs to add addition, then "addition" row is inserted: + // f0 f1 f2 f3 P0 P1 T0 T1 T2 T3 Q0 Q1 Q2 Q3 L0 L1 L2 L3 + // Gate 1: "addition" + // Constraints: + // 0. UT = untwisted T, UQ = untwisted Q + // 1. L = (UT.y - UQ.y) / (UT.x - UQ.x) + // 2. f_next = f*(L*(P.x - UT.x) - (P.y - UT.y)) + // 3. T_next = T + Q + // + // 219 rows total: 147 doubling and 71 addition + 1 row with result + // P is copied in addition and doubling rows of the circuit + // Q is copied only in addition rows. + // Initial value f (1,0,0,0) is copied from constants column + // + // Total number of copy constraints: 724 = 4+2*147+6*71 + // + // We can reduce number of copy constraints by next trick: + // 1. Copy Q in doubling rows too + // 2. To each gate (doubling and addition) add addition 6 constraints: + // w_i = w_i_rot(1), i = 2,4 (P), 9,10,11,12 (Q) + // 3. Leave copy constraints for P and Q on the first row + // Total number of copy constraints will be: + // 4+2+6 = 12 + // At the expense of adding 6 additional constraints to each gate + // + // Witnesses for L0 and L1 could be removed as they are always zero + + using namespace detail; + using detail::base; + + template + class mnt4_miller_loop; + + template + class mnt4_miller_loop> + : public plonk_component { + + public: + using component_type = plonk_component; + using integral_type = typename BlueprintFieldType::integral_type; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::size_t witness_amount; + + gate_manifest_type(std::size_t witness_amount_) : witness_amount(witness_amount_) {} + + std::uint32_t gates_amount() const override { + return mnt4_miller_loop::gates_amount; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount) + { + gate_manifest manifest = + gate_manifest(gate_manifest_type(witness_amount)); + return manifest; + } + + static manifest_type get_manifest() + { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(18)), + true // constant column required + ); + return manifest; + } + + static std::size_t get_rows_amount( + std::size_t witness_amount) + { + std::vector C_bin = base<2>(C_val); + + std::size_t result = 0; + + // doubling blocks x LineFunctions + result += C_bin.size()-1; + // adding blocks x LineFunctions + result += std::count(C_bin.begin(),C_bin.end(), 1) - 1; + // final result (for gate uniformity) + result += 1; + return result; + } + + constexpr static integral_type C_val = mnt4_pairing_params::ate_loop_count; + std::vector C_bin = base<2>(C_val); + + constexpr static const std::size_t gates_amount = 2; + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + + struct input_type { + std::array P; + std::array Q; + + std::vector> all_vars() { + return {P[0], P[1], Q[0], Q[1], Q[2], Q[3]}; + } + }; + + struct result_type { + std::array output; + + result_type(mnt4_miller_loop const& component, std::uint32_t start_row_index) { + std::size_t res_row = start_row_index + component.rows_amount - 1; + for(std::size_t i = 0; i < 4; i++) { + output[i] = var(component.W(i), res_row, false, var::column_type::witness); + } + } + + std::vector> all_vars() { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template + explicit mnt4_miller_loop(ContainerType witness) : + component_type(witness, {}, {}, get_manifest()) {}; + + template + mnt4_miller_loop( + WitnessContainerType witness, + ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + mnt4_miller_loop( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_miller_loop = + mnt4_miller_loop>; + + template + typename plonk_miller_loop::result_type + generate_assignments( + plonk_miller_loop const& component, + assignment> &assignment, + typename plonk_miller_loop::input_type const& instance_input, + const std::uint32_t start_row_index) + { + using component_type = plonk_miller_loop; + using var = typename component_type::var; + using value_type = typename BlueprintFieldType::value_type; + + using policy_type_fp2 = crypto3::algebra::fields::fp2; + using fp2_element = typename policy_type_fp2::value_type; + using curve_point = std::array; + + using curve_type = nil::crypto3::algebra::curves::mnt4<298>; + + value_type + xP = var_value(assignment, instance_input.P[0]), + yP = var_value(assignment, instance_input.P[1]); + + std::array + xQ = {var_value(assignment, instance_input.Q[0]), var_value(assignment, instance_input.Q[1])}, + yQ = {var_value(assignment, instance_input.Q[2]), var_value(assignment, instance_input.Q[3])}; + + curve_point Q = { fp2_element(xQ[0], xQ[1]), fp2_element(yQ[0], yQ[1])}; + + /* Calculate point doubling on E', affine coordinates */ + auto double_point = [](curve_point const& T) { + fp2_element a(curve_type::g2_type<>::params_type::a), + lambda = (3*T[0].pow(2) + a) / (2*T[1]), + xR = lambda.pow(2) - 2*T[0], + yR = (3*T[0])*lambda - lambda.pow(3) - T[1]; + return curve_point({xR, yR}); + }; + + /* Calculate point addition on E', affine coordinates */ + auto add_points = [](curve_point const& T, curve_point const& Q) { + fp2_element + lambda = (T[1] - Q[1])/(T[0] - Q[0]), + xR = lambda*lambda - T[0] - Q[0], + yR = (2*T[0] + Q[0])*lambda - lambda.pow(3) - T[1]; + return curve_point({xR, yR}); + }; + + using policy_type_fp4 = crypto3::algebra::fields::fp4; + using fp4_element = typename policy_type_fp4::value_type; + + auto insert_row_doubling = [ + &double_point, &assignment, &component, &start_row_index, &xP, &yP] + (fp4_element const& f, curve_point& T, std::size_t row) + { + fp4_element x, y, x1, y1, g, three({{3,0},{0,0}}); + + x = fp4_element({ {xP, 0}, {0, 0} }); + y = fp4_element({ {yP, 0}, {0, 0} }); + + // Untwisting: E'/Fp2 -> E/Fp4 + // x * u^-1, y * (uv)^-1 + // MNT4 nr = 0x11, + // u = (0,1), v = ((0,0), (1,0)) + // u^2 = nr, v^2 = u + // u = ((0,1),(0,0)), u^-1 = ((0,1/nr),(0,0)) + // v = ((0,0),(1,0)), (uv)^-1 = ((0,0), (1/nr, 0)) + // + // ((x0,x1),(x2,x3)) * ((0,1/nr),(0,0)) = ( (x1, x0/nr), (x3, x2/nr) ) + // ((x0,x1),(x2,x3)) * ((0,0),(1/nr,0)) = ( (x3, x2/nr), (x0/nr, x1/nr) ) + // + // T is in form (X,Y)/Fp2: + // X: ((x0,x1),(0,0)) * ((0,1/nr),(0,0)) = ( (x1, x0/nr), (0, 0) ) + // Y: ((y0,y1),(0,0)) * ((0,0),(1/nr,0)) = ( (0, 0), (y0/nr, y1/nr) ) + // + value_type nri = policy_type_fp2::extension_policy::non_residue.inversed(); + + x1 = fp4_element({ {T[0].data[1], T[0].data[0]*nri }, {0,0} }); + y1 = fp4_element({ {0,0}, {T[1].data[0]*nri, T[1].data[1]*nri}}); + + fp4_element a({{curve_type::g1_type<>::params_type::a,0},{0,0}}); + + fp4_element lf =(three*x1.pow(2) + a)*(y1+y1).inversed(); + g = f.pow(2) * (lf*(x-x1) + (y1-y)); + + // f + assignment.witness(component.W(0),start_row_index + row) = f.data[0].data[0]; + assignment.witness(component.W(1),start_row_index + row) = f.data[0].data[1]; + assignment.witness(component.W(2),start_row_index + row) = f.data[1].data[0]; + assignment.witness(component.W(3),start_row_index + row) = f.data[1].data[1]; + + // P + assignment.witness(component.W(4),start_row_index + row ) = xP; + assignment.witness(component.W(5),start_row_index + row ) = yP; + + // T <- T+T + assignment.witness(component.W(6),start_row_index + row) = T[0].data[0]; + assignment.witness(component.W(7),start_row_index + row) = T[0].data[1]; + assignment.witness(component.W(8),start_row_index + row) = T[1].data[0]; + assignment.witness(component.W(9),start_row_index + row) = T[1].data[1]; + T = double_point(T); + + // Q is not used in doubling rows + assignment.witness(component.W(10),start_row_index + row) = 0; + assignment.witness(component.W(11),start_row_index + row) = 0; + assignment.witness(component.W(12),start_row_index + row) = 0; + assignment.witness(component.W(13),start_row_index + row) = 0; + + // lf + assignment.witness(component.W(14),start_row_index + row) = lf.data[0].data[0]; + assignment.witness(component.W(15),start_row_index + row) = lf.data[0].data[1]; + assignment.witness(component.W(16),start_row_index + row) = lf.data[1].data[0]; + assignment.witness(component.W(17),start_row_index + row) = lf.data[1].data[1]; + return g; + }; + + auto insert_row_addition = [ + &add_points, &assignment, &component, &start_row_index, &xP, &yP, &Q] + (fp4_element const& f, curve_point& T, std::size_t row) + { + fp4_element x, y, x1, y1, x2, y2, lf, g; + + x = fp4_element({ {xP, 0}, {0, 0} }); + y = fp4_element({ {yP, 0}, {0, 0} }); + + value_type nri = policy_type_fp2::extension_policy::non_residue.inversed(); + + // Untwist T and Q: E'/Fp2 -> E/Fp4 + x1 = fp4_element({ {T[0].data[1], T[0].data[0]*nri }, {0, 0} }); + y1 = fp4_element({ {0, 0}, {T[1].data[0]*nri, T[1].data[1]*nri}}); + + x2 = fp4_element({ {Q[0].data[1], Q[0].data[0]*nri }, {0, 0} }); + y2 = fp4_element({ {0, 0}, {Q[1].data[0]*nri, Q[1].data[1]*nri}}); + + lf = (y2-y1)*(x2-x1).inversed(); + g = f * (lf*(x-x1) + y1 - y); + + // f + assignment.witness(component.W(0),start_row_index + row) = f.data[0].data[0]; + assignment.witness(component.W(1),start_row_index + row) = f.data[0].data[1]; + assignment.witness(component.W(2),start_row_index + row) = f.data[1].data[0]; + assignment.witness(component.W(3),start_row_index + row) = f.data[1].data[1]; + + // P + assignment.witness(component.W(4),start_row_index + row ) = xP; + assignment.witness(component.W(5),start_row_index + row ) = yP; + + // T <- T+Q + assignment.witness(component.W(6),start_row_index + row) = T[0].data[0]; + assignment.witness(component.W(7),start_row_index + row) = T[0].data[1]; + assignment.witness(component.W(8),start_row_index + row) = T[1].data[0]; + assignment.witness(component.W(9),start_row_index + row) = T[1].data[1]; + T = add_points(T, Q); + + // Q + assignment.witness(component.W(10),start_row_index + row) = Q[0].data[0]; + assignment.witness(component.W(11),start_row_index + row) = Q[0].data[1]; + assignment.witness(component.W(12),start_row_index + row) = Q[1].data[0]; + assignment.witness(component.W(13),start_row_index + row) = Q[1].data[1]; + + // lf + assignment.witness(component.W(14),start_row_index + row) = lf.data[0].data[0]; + assignment.witness(component.W(15),start_row_index + row) = lf.data[0].data[1]; + assignment.witness(component.W(16),start_row_index + row) = lf.data[1].data[0]; + assignment.witness(component.W(17),start_row_index + row) = lf.data[1].data[1]; + return g; + }; + + std::size_t rel_row = 0; + + fp4_element f = fp4_element::one(); + curve_point T = Q; + + /* Miller loop */ + for(std::size_t i = 1; i < component.C_bin.size(); ++i) { + f = insert_row_doubling(f, T, rel_row++); + if (component.C_bin[i]) { + f = insert_row_addition(f, T, rel_row++); + } + } + + // The last row contains the result, f. + // f + assignment.witness(component.W(0),start_row_index + rel_row) = f.data[0].data[0]; + assignment.witness(component.W(1),start_row_index + rel_row) = f.data[0].data[1]; + assignment.witness(component.W(2),start_row_index + rel_row) = f.data[1].data[0]; + assignment.witness(component.W(3),start_row_index + rel_row) = f.data[1].data[1]; + + /* + // P + assignment.witness(component.W(4),start_row_index + rel_row ) = xP; + assignment.witness(component.W(5),start_row_index + rel_row ) = yP; + */ + + /* T is needed as previous row has constraints on it */ + assignment.witness(component.W(6),start_row_index + rel_row) = T[0].data[0]; + assignment.witness(component.W(7),start_row_index + rel_row) = T[0].data[1]; + assignment.witness(component.W(8),start_row_index + rel_row) = T[1].data[0]; + assignment.witness(component.W(9),start_row_index + rel_row) = T[1].data[1]; + + /* + // Q + assignment.witness(component.W(10),start_row_index + rel_row) = Q[0].data[0]; + assignment.witness(component.W(11),start_row_index + rel_row) = Q[0].data[1]; + assignment.witness(component.W(12),start_row_index + rel_row) = Q[1].data[0]; + assignment.witness(component.W(13),start_row_index + rel_row) = Q[1].data[1]; + */ + + return typename plonk_miller_loop::result_type( + component, start_row_index); + } + + template + std::vector + generate_gates( + plonk_miller_loop const& component, + circuit> &bp, + assignment> &assignment, + const typename plonk_miller_loop::input_type &instance_input) + { + using var = typename plonk_miller_loop::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using curve_type = nil::crypto3::algebra::curves::mnt4<298>; + using policy_type_fp2 = crypto3::algebra::fields::fp2; + + using fp2_constraint = detail::abstract_fp2_element< + constraint_type, BlueprintFieldType>; + + using fp4_constraint = detail::abstract_fp4_element< + constraint_type, BlueprintFieldType>; + + using value_type = typename BlueprintFieldType::value_type; + + fp2_constraint C; + fp4_constraint C4; + + std::vector gate_list = {}; + constraint_type c_zero = constraint_type(), c_one = c_zero + 1; + constraint_type c_g1_a = c_zero + curve_type::g1_type<>::params_type::a; + constraint_type c_g2_a0 = c_zero + curve_type::g2_type<>::params_type::a.data[0]; + constraint_type c_g2_a1 = c_zero + curve_type::g2_type<>::params_type::a.data[1]; + value_type nri = policy_type_fp2::extension_policy::non_residue.inversed(); + + /* Constraints for the doubling gate + * 1. f = f_prev^2 * line_function_doubling(T,T,P) + * 2. T_next = T + T + */ + std::vector doubling_constrs = {}; + { + fp4_constraint + one4 = {c_one, c_zero, c_zero, c_zero }, + a4 = { c_g1_a, c_zero, c_zero, c_zero, }, + f = { + var(component.W(0), 0, true), + var(component.W(1), 0, true), + var(component.W(2), 0, true), + var(component.W(3), 0, true) + }, + fnext = { + var(component.W(0), 1, true), + var(component.W(1), 1, true), + var(component.W(2), 1, true), + var(component.W(3), 1, true) + }, + x = { + var(component.W(4), 0, true), + c_zero, c_zero, c_zero + }, + y = { + var(component.W(5), 0, true), + c_zero, c_zero, c_zero + }, + x1 = { + var(component.W(7), 0, true), + var(component.W(6), 0, true) * nri, + c_zero, c_zero + }, + y1 = { + c_zero, c_zero, + var(component.W(8), 0, true) * nri, + var(component.W(9), 0, true) * nri + }, + lf = { + var(component.W(14), 0, true), + var(component.W(15), 0, true), + var(component.W(16), 0, true), + var(component.W(17), 0, true) + }; + + C4 = lf*(2*y1) - (3*x1*x1 + a4); + doubling_constrs.push_back(C4[0]); + doubling_constrs.push_back(C4[1]); + doubling_constrs.push_back(C4[2]); + doubling_constrs.push_back(C4[3]); + + C4 = fnext - f*f*(lf*(x - x1) - (y - y1)); + doubling_constrs.push_back(C4[0]); + doubling_constrs.push_back(C4[1]); + doubling_constrs.push_back(C4[2]); + doubling_constrs.push_back(C4[3]); + } + + /* Constraints for point doubling: Tnext = T + T: + * Tnext.x = (3*T.x^2+a)^2/(2T.y)^2 - 2T.x + * Tnext.y = (3*T.x^2+a)/2T.y *(T.x-Tnext.x)-T.y + * Rewrite: + * (Tnext.x + 2*Tx) * (2*T.y)^2 - (3*T.x^2+a)^2 = 0 + * (Tnext.y + T.y) * (2*T.y) - (3*T.x^2+a)*(T.x-Tnext.x) = 0 + */ + fp2_constraint + a = {c_g2_a0, c_g2_a1}, + Tx = {var(component.W(6), 0, true), var(component.W(7), 0, true)}, + Ty = {var(component.W(8), 0, true), var(component.W(9), 0, true)}, + Tnx = {var(component.W(6), 1, true), var(component.W(7), 1, true)}, + Tny = {var(component.W(8), 1, true), var(component.W(9), 1, true)}; + + C = (Tnx + 2*Tx)*(2*Ty)*(2*Ty) - (3*Tx*Tx + a)*(3*Tx*Tx + a); + doubling_constrs.push_back(C[0]); + doubling_constrs.push_back(C[1]); + + C = (Tny + Ty)*(2*Ty) - (3*Tx*Tx + a)*(Tx - Tnx); + doubling_constrs.push_back(C[0]); + doubling_constrs.push_back(C[1]); + + gate_list.push_back(bp.add_gate(doubling_constrs)); + + /* Constraints for the addition row + * 1. f = f_prev * line_function_addition(T,Q,P) + * 2. T = T_prev + Q + */ + std::vector adding_constrs = {}; + { + fp4_constraint + f = { + var(component.W(0), 0, true), + var(component.W(1), 0, true), + var(component.W(2), 0, true), + var(component.W(3), 0, true) + }, + fnext = { + var(component.W(0), 1, true), + var(component.W(1), 1, true), + var(component.W(2), 1, true), + var(component.W(3), 1, true) + }, + x = { + var(component.W(4), 0, true), + c_zero, c_zero, c_zero + }, + y = { + var(component.W(5), 0, true), + c_zero, c_zero, c_zero + }, + x1 = { + var(component.W(7), 0, true), + var(component.W(6), 0, true) * nri, + c_zero, c_zero + }, + y1 = { + c_zero, c_zero, + var(component.W(8), 0, true) * nri, + var(component.W(9), 0, true) * nri + }, + x2 = { + var(component.W(11), 0, true), + var(component.W(10), 0, true) * nri, + c_zero, c_zero + }, + y2 = { + c_zero, c_zero, + var(component.W(12), 0, true) * nri, + var(component.W(13), 0, true) * nri + }, + lf = { + var(component.W(14), 0, true), + var(component.W(15), 0, true), + var(component.W(16), 0, true), + var(component.W(17), 0, true) + }; + + C4 = lf*(x2 - x1) - (y2 - y1); + adding_constrs.push_back(C4[0]); + adding_constrs.push_back(C4[1]); + adding_constrs.push_back(C4[2]); + adding_constrs.push_back(C4[3]); + + C4 = fnext - f*(lf*(x-x1) - (y-y1)); + adding_constrs.push_back(C4[0]); + adding_constrs.push_back(C4[1]); + adding_constrs.push_back(C4[2]); + adding_constrs.push_back(C4[3]); + } + + /* Constraints for point addition: Tnext = T + Q: + * Tnext.x = (Q.y - T.y)^2/(Q.x - T.x)^2- T.x - Q.x + * Tnext.y = (Q.y - T.y)/(Q.x - T.x)*(T.x - Tnext.x) - T.y + * Rewrite: + * (Tnext.x + T.x + Q.x)*(Q.x - T.x)^2 - (Q.y - T.y)^2 = 0 + * (Tnext.y + T.y)*(Q.x - T.x) - (Q.y - T.y) * (T.x - Tnext.x) = 0 + */ + fp2_constraint + Qx = {var(component.W(10), 0, true), var(component.W(11), 0, true)}, + Qy = {var(component.W(12), 0, true), var(component.W(13), 0, true)}; + + C = (Tnx + Tx + Qx)*(Qx - Tx)*(Qx - Tx) - (Qy - Ty)*(Qy - Ty); + adding_constrs.push_back(C[0]); + adding_constrs.push_back(C[1]); + + C = (Tny + Ty)*(Qx - Tx) - (Qy - Ty)*(Tx - Tnx); + adding_constrs.push_back(C[0]); + adding_constrs.push_back(C[1]); + + gate_list.push_back(bp.add_gate(adding_constrs)); + + return gate_list; + } + + template + void generate_copy_constraints( + plonk_miller_loop const& component, + circuit> &bp, + assignment> &assignment, + const typename plonk_miller_loop::input_type &instance_input, + const std::size_t start_row_index) + { + + using component_type = plonk_miller_loop; + using var = typename component_type::var; + + /* Copy constraints for f in the first row to constants (1,0,0,0) in constant column */ + bp.add_copy_constraint({ + var(0, start_row_index + 1, false, var::column_type::constant), + var(component.W(0), start_row_index, false)}); + bp.add_copy_constraint({ + var(0, start_row_index, false, var::column_type::constant), + var(component.W(1), start_row_index, false)}); + bp.add_copy_constraint({ + var(0, start_row_index, false, var::column_type::constant), + var(component.W(2), start_row_index, false)}); + bp.add_copy_constraint({ + var(0, start_row_index, false, var::column_type::constant), + var(component.W(3), start_row_index, false)}); + + /* T on the first row is Q */ + bp.add_copy_constraint({var(component.W(6), start_row_index, false), instance_input.Q[0]}); + bp.add_copy_constraint({var(component.W(7), start_row_index, false), instance_input.Q[1]}); + bp.add_copy_constraint({var(component.W(8), start_row_index, false), instance_input.Q[2]}); + bp.add_copy_constraint({var(component.W(9), start_row_index, false), instance_input.Q[3]}); + + std::size_t row = 0; + + /* Copy P and Q along the circuit */ + for(std::size_t i = 1; i < component.C_bin.size(); ++i) { + // P + bp.add_copy_constraint({var(component.W(4), start_row_index + row, false), instance_input.P[0]}); + bp.add_copy_constraint({var(component.W(5), start_row_index + row, false), instance_input.P[1]}); + ++row; + + if (component.C_bin[i]) { + // P + bp.add_copy_constraint({var(component.W(4), start_row_index + row, false), instance_input.P[0]}); + bp.add_copy_constraint({var(component.W(5), start_row_index + row, false), instance_input.P[1]}); + // Q + bp.add_copy_constraint({var(component.W(10), start_row_index + row, false), instance_input.Q[0]}); + bp.add_copy_constraint({var(component.W(11), start_row_index + row, false), instance_input.Q[1]}); + bp.add_copy_constraint({var(component.W(12), start_row_index + row, false), instance_input.Q[2]}); + bp.add_copy_constraint({var(component.W(13), start_row_index + row, false), instance_input.Q[3]}); + ++row; + } + } + } + + template + typename plonk_miller_loop::result_type + generate_circuit( + plonk_miller_loop const& component, + circuit> &bp, + assignment> &assignment, + const typename plonk_miller_loop::input_type &instance_input, + const std::size_t start_row_index) + { + using component_type = plonk_miller_loop; + using var = typename component_type::var; + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + generate_assignments_constant(component, assignment, instance_input, start_row_index); + + std::size_t row = 0; + for(std::size_t i = 1; i < component.C_bin.size(); i++) { + assignment.enable_selector(selector_index[0], start_row_index + row); + ++row; + if (component.C_bin[i]) { + assignment.enable_selector(selector_index[1], start_row_index + row); + ++row; + } + } + + return typename plonk_miller_loop::result_type( + component, start_row_index); + } + + template + void generate_assignments_constant( + const plonk_miller_loop &component, + assignment> &assignment, + const typename plonk_miller_loop::input_type &instance_input, + const std::size_t start_row_index) { + + // '0' to make copy-constraints with + assignment.constant(component.C(0), start_row_index) = 0; + // '1' to make copy-constraints with + assignment.constant(component.C(0), start_row_index + 1) = 1; + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MILLER_LOOP_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 23fc015d1..bab8a9b70 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -80,6 +80,7 @@ set(NON_NATIVE_TESTS_FILES "algebra/fields/plonk/non_native/comparison_flag" "algebra/fields/plonk/non_native/equality_flag" "algebra/fields/plonk/non_native/division_remainder" + "algebra/fields/plonk/non_native/mnt4_fp4_fixed_power" "non_native/plonk/bool_scalar_multiplication" "non_native/plonk/add_mul_zkllvm_compatible" "non_native/plonk/scalar_non_native_range" @@ -188,6 +189,8 @@ set(HASHES_TESTS_FILES "hashes/r1cs/pedersen") set(PAIRING_TESTS_FILES + "algebra/pairing/weierstrass/plonk/mnt4_pairing" + "algebra/pairing/weierstrass/plonk/mnt4_exponentiation" "algebra/pairing/weierstrass/r1cs/miller_loop" "algebra/pairing/weierstrass/r1cs/precomputation") diff --git a/test/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.cpp b/test/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.cpp new file mode 100644 index 000000000..536620f2b --- /dev/null +++ b/test/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.cpp @@ -0,0 +1,160 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// +// exponentiation to a fixed power in Fp4 for MNT4 GT +// +#define BOOST_TEST_MODULE blueprint_plonk_mnt4_fp4_fixed_power_test + +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_mnt4_fp4_fixed_power( + std::vector public_input, + typename CurveType::base_field_type::extended_integral_type power, + typename CurveType::gt_type::value_type expected_res) +{ + using curve_type = CurveType; + using BlueprintFieldType = typename curve_type::base_field_type; + + constexpr std::size_t WitnessColumns = WitnessAmount; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 5; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::mnt4_fp4_fixed_power; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), + var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), + var(0, 3, false, var::column_type::public_input) + }; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "GT (MNT4_FP4) fixed power test: " << "\n"; + std::cout << "input : " << public_input[0].data << "," << public_input[1].data << "\n"; + std::cout << "input : " << public_input[2].data << "," << public_input[3].data << "\n"; + std::cout << "expected: " << expected_res.data[0].data[0] << "," << expected_res.data[0].data[1] << ",\n"; + std::cout << " : " << expected_res.data[1].data[0] << "," << expected_res.data[1].data[1] << ",\n"; + std::cout << "real : " << var_value(assignment, real_res.output[0]).data << "," << var_value(assignment, real_res.output[1]).data << ",\n"; + std::cout << " " << var_value(assignment, real_res.output[2]).data << "," << var_value(assignment, real_res.output[3]).data << "\n\n"; + #endif + assert(expected_res.data[0].data[0] == var_value(assignment, real_res.output[0])); + assert(expected_res.data[0].data[1] == var_value(assignment, real_res.output[1])); + assert(expected_res.data[1].data[0] == var_value(assignment, real_res.output[2])); + assert(expected_res.data[1].data[1] == var_value(assignment, real_res.output[3])); + return true; + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance(witnesses, + std::array{}, + std::array{}, + power); + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, + power); +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt4_fp4_fixed_power_test) { + using curve_type = crypto3::algebra::curves::mnt4_298; + using group_type = typename curve_type::gt_type; + using base_field_value = curve_type::base_field_type::value_type; + + typedef typename group_type::value_type group_value_type; + typedef typename group_type::underlying_field_type::value_type underlying_field_type; + typedef typename group_type::base_field_type::value_type field_value_type; + typedef typename group_type::base_field_type::integral_type integral_type; + + std::vector test_gt_elems = { + group_value_type( + underlying_field_type( +// 4,3 + 0x22c26a3c19d56fc8790485554be5dc4351961a5162c3634965dc8ae56701157e_cppui254, + 0x1e3305b98bf381650491b7b63559d20d662b70f1616e680a19170715b59a3426_cppui254 + ), + underlying_field_type( +// 2,1 + 0x148a1f438a4cd0d807549cb7f9ec9f41dba3d8b14a6b0f2489d9b9f626d6fd31_cppui254, + 0x3cc907ef65b0eff91d027e4771e9116a0b125325627b6bdf55037702220b1b2_cppui254 + ) + ), + }; + + //auto fixed_power = pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0; + auto fixed_power = pairing::detail::pairing_params::final_exponent; +// auto fixed_power = integral_type("100"); + + for(std::size_t i = 0; i < test_gt_elems.size(); i++) { + std::cout << "Test instance # " << (i+1) << "\n"; + group_value_type P = test_gt_elems[i]; + group_value_type R = P.pow(fixed_power); + + test_mnt4_fp4_fixed_power( + std::vector{ P.data[0].data[0], P.data[0].data[1], P.data[1].data[0], P.data[1].data[1] }, + fixed_power, + R); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.cpp b/test/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.cpp new file mode 100644 index 000000000..9ee6868c7 --- /dev/null +++ b/test/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.cpp @@ -0,0 +1,138 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_pairing_mnt4_298 + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; +using namespace blueprint::components::detail; + +template +void test_mnt4_298_exponentiation(std::vector public_input, + std::vector expected_res) { + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 8; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment; + + using value_type = typename FieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::mnt4_exponentiation; + + typename component_type::input_type instance_input = { + var(0,0, false, var::column_type::public_input), // f[0] + var(0,1, false, var::column_type::public_input), // f[1] + var(0,2, false, var::column_type::public_input), // f[2] + var(0,3, false, var::column_type::public_input), // f[3] + }; + + auto result_check = [&expected_res](AssignmentType const& assignment, + typename component_type::result_type const& real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "MNT4-298 Final exponentiation: expected res VS output\n"; + for(std::size_t i = 0; i < 4; i++) { + std::cout << std::dec << expected_res[i].data << " =? " << var_value(assignment, real_res.output[i]).data << "\n"; + } + #endif + for(std::size_t i = 0; i < 4; i++) { + assert(expected_res[i] == var_value(assignment, real_res.output[i])); + } + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance(witnesses, // witnesses + std::array{}, // constants + std::array{} // public inputs + ); + + nil::crypto3::test_component ( + component_instance, desc, public_input, result_check, instance_input); +} + +static const std::size_t random_tests_amount = 5; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt4_pairing_test) { + using curve_type = crypto3::algebra::curves::mnt4_298; + using gt_group_type = typename curve_type::gt_type; + using base_field_value = curve_type::base_field_type::value_type; + using field_type = typename curve_type::gt_type::base_field_type; + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + std::vector + AB_ML = { + 0x01f3f02a39499cca91c7c3a108cc0721047455bc2def95bcb613a1749c1bbe0fb0d88088699b_cppui298, + 0x00eeaea28cc850898e84e44ec6ae59fb0079c1d1c93f0f6e30c541695c45c4b9e3df07db6b77_cppui298, + 0x030ffc05991f5ac550b3c37b44c253e2c2a39359ec30d0ec24f4c42e3a6b937d02653c92e7d5_cppui298, + 0x001e339f66ccb68e51bab41310a5d30d2f51a002ae9b7bcf506c0c44447c725f0e2b618e9571_cppui298 + }, + AB_FE = { + 0x01fbabaf3b011714d2d119340016213db9fb8b5eeefbf32a082b4f8ee40cb6f79825d082f2b0_cppui298, + 0x038ae7fa7f91cc2ab005a8ea5c8c475820d848c5f0aa942a27da982b18fe96514c682a9bb227_cppui298, + 0x00fed8ea05b7600bea786cb3bb779876cb8623e11466112530237b9a2d296b4367033969b515_cppui298, + 0x008957e24bff45f132925d25383fbd0c1e1a0bea95fdae5346b0f42b2b44fbe3a80ce4a49d98_cppui298 + }; + + std::cout << "MNT4-298 Final exponentiation test\n"; + test_mnt4_298_exponentiation(AB_ML, AB_FE); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/algebra/pairing/weierstrass/plonk/mnt4_pairing.cpp b/test/algebra/pairing/weierstrass/plonk/mnt4_pairing.cpp new file mode 100644 index 000000000..9c3e6947d --- /dev/null +++ b/test/algebra/pairing/weierstrass/plonk/mnt4_pairing.cpp @@ -0,0 +1,147 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_pairing_mnt4_298 + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; +using namespace blueprint::components::detail; + +template +void test_mnt4_298_miller_loop(std::vector public_input, + std::vector expected_res) { + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = (WitnessColumns == 12)? (4 + 8) : (4 + 9); + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment; + + using value_type = typename FieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::mnt4_miller_loop; + + typename component_type::input_type instance_input = { + var(0,0, false, var::column_type::public_input), // xP + var(0,1, false, var::column_type::public_input), // yP + var(0,2, false, var::column_type::public_input), // xQ[0] + var(0,3, false, var::column_type::public_input), // xQ[1] + var(0,4, false, var::column_type::public_input), // yQ[0] + var(0,5, false, var::column_type::public_input) // yQ[1] + }; + + auto result_check = [&expected_res](AssignmentType const& assignment, + typename component_type::result_type const& real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "MNT4-298 Miller loop: expected res VS output\n"; + for(std::size_t i = 0; i < 4; i++) { + std::cout << std::dec << expected_res[i].data << " =? " << var_value(assignment, real_res.output[i]).data << "\n"; + } + #endif + for(std::size_t i = 0; i < 4; i++) { + assert(expected_res[i] == var_value(assignment, real_res.output[i])); + } + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance(witnesses, // witnesses + std::array{0}, // constants + std::array{} // public inputs + ); + + nil::crypto3::test_component ( + component_instance, desc, public_input, result_check, instance_input); +} + +static const std::size_t random_tests_amount = 5; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt4_pairing_test) { + using curve_type = crypto3::algebra::curves::mnt4_298; + using g2_group_type = typename curve_type::g2_type<>; + using base_field_value = curve_type::base_field_type::value_type; + using field_type = typename curve_type::g2_type<>::field_type::base_field_type; + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + std::vector + AB = { + // A :[ + 0x02ee42725289d05230b6700ba1696044a839c30e114c65ab98d5d0764b9c79c06d207c5f3d12_cppui298, + 0x0377ea5dd66341cb88b291c103eec911946c7266fabbda487ef3ac48e5954253df56f89ebe49_cppui298, + // ] + + // B :[ [ + 0x012212f4ac5a2b6262dcd15a0fb4e54d276d734d80e3868dc93a074b3a9ebeb598641aa2310d_cppui298, + 0x017600e8757679e06b66de2c48b3370e582443d4c0091ef1e6d96dadb92150ff642709dd806b_cppui298, + 0x02a1135b45f576b0988c2f5e852def5e829508beddae07427cc68929ffbeaa49de4d370cfa69_cppui298, + 0x0246c479956c92096a1dfa7cdb992b53ecb05f96d581fcb755045898fb459fd569753da2c2a7_cppui298 + // ]] + }, + AB_ML = { + 0x01f3f02a39499cca91c7c3a108cc0721047455bc2def95bcb613a1749c1bbe0fb0d88088699b_cppui298, + 0x00eeaea28cc850898e84e44ec6ae59fb0079c1d1c93f0f6e30c541695c45c4b9e3df07db6b77_cppui298, + 0x030ffc05991f5ac550b3c37b44c253e2c2a39359ec30d0ec24f4c42e3a6b937d02653c92e7d5_cppui298, + 0x001e339f66ccb68e51bab41310a5d30d2f51a002ae9b7bcf506c0c44447c725f0e2b618e9571_cppui298 + }; + + std::cout << "MNT4-298 Miller loop test\n"; + test_mnt4_298_miller_loop(AB, AB_ML); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/test_plonk_component.hpp b/test/test_plonk_component.hpp index b51a5a369..d7d7de74e 100644 --- a/test/test_plonk_component.hpp +++ b/test/test_plonk_component.hpp @@ -57,7 +57,7 @@ #include -// #include "profiling_plonk_circuit.hpp" +//#include "profiling_plonk_circuit.hpp" #include #include @@ -252,7 +252,15 @@ namespace nil { variable.get() = assignment.get_batch_variable_map().at(variable); } } - +/* + std::ofstream fass("circuit.tbl"); + assignment.export_table(fass); + fass.close(); + + std::ofstream fcirc("circuit.crt"); + bp.export_circuit(fcirc); + fcirc.close(); +*/ result_check(assignment, component_result); if constexpr (!PrivateInput) { @@ -309,15 +317,16 @@ namespace nil { } desc.rows_amount = zk::snark::basic_padding(assignment); -#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED +//#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED std::cout << "Usable rows: " << desc.usable_rows_amount << std::endl; std::cout << "Padded rows: " << desc.rows_amount << std::endl; - profiling(assignment); -#endif +// profiling(assignment); +//#endif //assignment.export_table(std::cout); //bp.export_circuit(std::cout); + assert(blueprint::is_satisfied(bp, assignment) == expected_to_pass); return std::make_tuple(desc, bp, assignment); @@ -621,4 +630,4 @@ namespace nil { } // namespace crypto3 } // namespace nil -#endif // CRYPTO3_TEST_PLONK_COMPONENT_HPP \ No newline at end of file +#endif // CRYPTO3_TEST_PLONK_COMPONENT_HPP From cd1edb9fc1d818be7967bcbd3124add6ace3ad68 Mon Sep 17 00:00:00 2001 From: Vasiliy Olekhov Date: Mon, 22 Apr 2024 09:54:43 +0300 Subject: [PATCH 2/7] cmake submodule sync with master #355 --- cmake/modules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules b/cmake/modules index 5e6b354ea..a016a4216 160000 --- a/cmake/modules +++ b/cmake/modules @@ -1 +1 @@ -Subproject commit 5e6b354eaa69f6a275992e08220e925c34ba0a19 +Subproject commit a016a42163a58ef68bbde48082d0ddafe68b8e56 From 059533ff8a6a4f58bb3c35ed01867b5c19f97268 Mon Sep 17 00:00:00 2001 From: Vasiliy Olekhov Date: Mon, 22 Apr 2024 14:53:24 +0300 Subject: [PATCH 3/7] update naming #355 --- .../algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp index ba59aacb3..7d2eb26d8 100644 --- a/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp @@ -40,7 +40,7 @@ #include #include -#include +#include namespace nil { namespace blueprint { @@ -68,7 +68,7 @@ namespace nil { using integral_type = typename BlueprintFieldType::integral_type; using extended_integral_type = typename BlueprintFieldType::extended_integral_type; using fp4_element = typename policy_type::value_type; - using fp4_constraint = detail::mnt4_fp4_element; + using fp4_constraint = detail::abstract_fp4_element; private: static std::vector base4(extended_integral_type x) { From 56221dbe880a8ca3dad55e367b360a069c785009 Mon Sep 17 00:00:00 2001 From: Vasiliy Olekhov Date: Fri, 26 Apr 2024 13:57:36 +0300 Subject: [PATCH 4/7] Refactored MNT4 code and added MNT6 exponentiation #355 --- .../plonk/non_native/detail/abstract_fp6.hpp | 98 ++++ .../plonk/non_native/mnt4_fp4_fixed_power.hpp | 53 +- .../plonk/non_native/mnt6_fp6_fixed_power.hpp | 72 +-- .../weierstrass/plonk/mnt4_exponentiation.hpp | 9 +- .../weierstrass/plonk/mnt6_exponentiation.hpp | 492 ++++++++++++++++++ test/CMakeLists.txt | 2 + .../plonk/non_native/mnt4_fp4_fixed_power.cpp | 6 +- .../plonk/non_native/mnt6_fp6_fixed_power.cpp | 174 +++++++ .../weierstrass/plonk/mnt6_exponentiation.cpp | 152 ++++++ test/test_plonk_component.hpp | 8 +- 10 files changed, 992 insertions(+), 74 deletions(-) create mode 100644 include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp6.hpp create mode 100644 include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.hpp create mode 100644 test/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.cpp create mode 100644 test/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.cpp diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp6.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp6.hpp new file mode 100644 index 000000000..950e70aa5 --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp6.hpp @@ -0,0 +1,98 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of F_p^6 elements over an abstract entity (to be used with constraints) +// with F_p^6 = Fp^2 over Fp^3: +// Fp^6 = Fp^2[x]/(x^2 - u), u = (0,1,0) +// Fp^3 = Fp[y]/(y^3 - v), for MNT6: v = 5 +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP4_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP4_HPP + +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + + template + class abstract_fp6_element{ + public: + using policy_type_fp6 = crypto3::algebra::fields::fp6_2over3; + std::array x; + + T& operator[](std::size_t idx) { + return x[idx]; + } + const T& operator[](std::size_t idx) const { + return x[idx]; + } + + constexpr abstract_fp6_element operator*(abstract_fp6_element const& y) { + // Devegili et al - Multiplication and squaring in pairing-friendly fields + // https://eprint.iacr.org/2006/471.pdf, page 15, direct sextic + // Take note on isomorphism between a (Fp 2 over 3) and c (direct sextic) + // Indices map is on page 17 + constexpr std::size_t + _0 = 0, _1 = 3, _2 = 1, + _3 = 4, _4 = 2, _5 = 5; + + constexpr auto s = policy_type_fp6::extension_policy::non_residue; + + T c[6] = { + x[_0]*y[_0] + s*(x[_1]*y[_5] + x[_2]*y[_4] + x[_3]*y[_3] + x[_4]*y[_2] + x[_5]*y[_1]), + x[_0]*y[_1] + x[_1]*y[_0] + s*(x[_2]*y[_5] + x[_3]*y[_4] + x[_4]*y[_3] + x[_5]*y[_2]), + x[_0]*y[_2] + x[_1]*y[_1] + x[_2]*y[_0] + s*(x[_3]*y[_5] + x[_4]*y[_4] + x[_5]*y[_3]), + x[_0]*y[_3] + x[_1]*y[_2] + x[_2]*y[_1] + x[_3]*y[_0] + s*(x[_4]*y[_5] + x[_5]*y[_4]), + x[_0]*y[_4] + x[_1]*y[_3] + x[_2]*y[_2] + x[_3]*y[_1] + x[_4]*y[_0] + s* x[_5]*y[_5], + x[_0]*y[_5] + x[_1]*y[_4] + x[_2]*y[_3] + x[_3]*y[_2] + x[_4]*y[_1] + x[_5]*y[_0] + }; + + return { c[0], c[2], c[4], c[1], c[3], c[5]}; + } + + constexpr abstract_fp6_element operator*(const int a) { + return { x[0]*a, x[1]*a, x[2]*a, x[3]*a, x[4]*a, x[5]*a }; + } + friend abstract_fp6_element operator*(const int a, abstract_fp6_element const& x) { + return { x[0]*a, x[1]*a, x[2]*a, x[3]*a, x[4]*a, x[5]*a }; + } + constexpr abstract_fp6_element operator+(abstract_fp6_element const& y) { + return { x[0] + y[0], x[1] + y[1], x[2] + y[2], x[3] + y[3], x[4] + y[4], x[5] + y[5]}; + } + constexpr abstract_fp6_element operator-(abstract_fp6_element const& y) { + return { x[0] - y[0], x[1] - y[1], x[2] - y[2], x[3] - y[3], x[4] - y[4], x[5] - y[5]}; + } + }; + + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_abstract_FP4_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp index 7d2eb26d8..78e715171 100644 --- a/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp @@ -66,12 +66,11 @@ namespace nil { using constraint_type = crypto3::zk::snark::plonk_constraint; using policy_type = crypto3::algebra::fields::fp4; using integral_type = typename BlueprintFieldType::integral_type; - using extended_integral_type = typename BlueprintFieldType::extended_integral_type; using fp4_element = typename policy_type::value_type; using fp4_constraint = detail::abstract_fp4_element; private: - static std::vector base4(extended_integral_type x) { + static std::vector base4(integral_type x) { if (x > 0) { std::vector res = {std::uint8_t(x % 4)}; x /= 4; @@ -85,7 +84,7 @@ namespace nil { } } - static std::size_t gates_amount_internal(extended_integral_type power) { + static std::size_t gates_amount_internal(integral_type power) { std::size_t gates = 1; // at least one for power-4 operations std::vector exp_plan = base4(power); if (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1) { @@ -100,28 +99,28 @@ namespace nil { public: using manifest_type = plonk_component_manifest; - const extended_integral_type power/* = pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0*/; + const integral_type power/* = pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0*/; const std::vector exp_plan, exp_precompute; const std::size_t rows_amount; class gate_manifest_type : public component_gate_manifest { - const extended_integral_type power; - std::array gates_footprint(extended_integral_type power) const { + const integral_type power; + std::array gates_footprint(integral_type power) const { std::vector exp_plan = base4(power); return { (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1), (std::count(exp_plan.begin(),exp_plan.end(),3) > 0), (std::count(exp_plan.begin(),exp_plan.end(),2) > 0) }; } public: - gate_manifest_type(extended_integral_type power) : power(power) {} + gate_manifest_type(integral_type power) : power(power) {} std::uint32_t gates_amount() const override { return mnt4_fp4_fixed_power::gates_amount_internal(power); } bool operator<(const component_gate_manifest *other) const override { - extended_integral_type o_power = dynamic_cast(other)->power; + integral_type o_power = dynamic_cast(other)->power; std::array gates = gates_footprint(power), @@ -132,8 +131,7 @@ namespace nil { static gate_manifest get_gate_manifest( std::size_t witness_amount, - std::size_t lookup_column_amount, - extended_integral_type power) + integral_type power) { static gate_manifest manifest = gate_manifest(gate_manifest_type(power)); return manifest; @@ -141,7 +139,7 @@ namespace nil { static manifest_type get_manifest() { static manifest_type manifest = manifest_type( - std::shared_ptr(new manifest_range_param(4, 300, 1)), + std::shared_ptr(new manifest_single_value_param(policy_type::arity)), false ); return manifest; @@ -159,7 +157,9 @@ namespace nil { return precompute; } - static std::size_t get_rows_amount(extended_integral_type power) + static std::size_t get_rows_amount( + std::size_t witness_amount, + integral_type power) { std::vector exp_plan = base4(power), @@ -208,12 +208,12 @@ namespace nil { template explicit mnt4_fp4_fixed_power( ContainerType witness, - extended_integral_type power) : + integral_type power) : component_type(witness, {}, {}, get_manifest()), power(power), exp_plan(base4(power)), exp_precompute(get_precomputed_exps(exp_plan)), - rows_amount(get_rows_amount(power)) + rows_amount(get_rows_amount(this->witness_amount(), power)) { }; template @@ -221,31 +221,33 @@ namespace nil { WitnessContainerType witness, ConstantContainerType constant, PublicInputContainerType public_input, - extended_integral_type power) : + integral_type power) : component_type(witness, constant, public_input, get_manifest()), power(power), exp_plan(base4(power)), exp_precompute(get_precomputed_exps(exp_plan)), - rows_amount(get_rows_amount(power)) + rows_amount(get_rows_amount(this->witness_amount(), power)) { }; mnt4_fp4_fixed_power( std::initializer_list witnesses, std::initializer_list constants, std::initializer_list public_inputs, - extended_integral_type power) : + integral_type power) : component_type(witnesses, constants, public_inputs, get_manifest()), power(power), exp_plan(base4(power)), exp_precompute(get_precomputed_exps(exp_plan)), - rows_amount(get_rows_amount(power)) + rows_amount(get_rows_amount(this->witness_amount(), power)) { }; }; /* */ template - using component_type = mnt4_fp4_fixed_power, BlueprintFieldType>; + using component_type = mnt4_fp4_fixed_power< + crypto3::zk::snark::plonk_constraint_system, + BlueprintFieldType>; template typename component_type::result_type @@ -275,20 +277,9 @@ namespace nil { std::size_t row = 0; auto fill_row = [&component, &assignment, &start_row_index, &row](fp4_element const& V) { - value_type d00 = V.data[0].data[0]; - value_type d01 = V.data[0].data[1]; - value_type d10 = V.data[1].data[0]; - value_type d11 = V.data[1].data[1]; - assignment.witness(component.W(0),start_row_index + row) = d00; - assignment.witness(component.W(1),start_row_index + row) = d01; - assignment.witness(component.W(2),start_row_index + row) = d10; - assignment.witness(component.W(3),start_row_index + row) = d11; - - /* - for(std::size_t i = 0; i < policy_type::arity; i++) { + for(std::size_t i = 0; i < 4; i++) { assignment.witness(component.W(i),start_row_index + row) = V.data[i/2].data[i % 2]; } - */ row++; }; diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp index 1ede12082..755449b06 100644 --- a/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp @@ -40,7 +40,7 @@ #include #include -#include +#include namespace nil { namespace blueprint { @@ -52,11 +52,11 @@ namespace nil { // Output: y[6]: y = x^t as elements of F_p^6 // - template + template class mnt6_fp6_fixed_power; template - class mnt6_fp6_fixed_power> + class mnt6_fp6_fixed_power, BlueprintFieldType> : public plonk_component { public: @@ -67,7 +67,7 @@ namespace nil { using policy_type = crypto3::algebra::fields::fp6_2over3; using integral_type = typename policy_type::extension_policy::integral_type; using fp6_element = typename policy_type::value_type; - using fp6_constraint = detail::mnt6_fp6_element; + using fp6_constraint = detail::abstract_fp6_element; private: static std::vector base4(integral_type x) { @@ -105,6 +105,7 @@ namespace nil { const std::size_t rows_amount; class gate_manifest_type : public component_gate_manifest { + const integral_type power; std::array gates_footprint(integral_type power) const { std::vector exp_plan = base4(power); return { (exp_plan.size() - std::count(exp_plan.begin(),exp_plan.end(),0) > 1), @@ -112,7 +113,6 @@ namespace nil { (std::count(exp_plan.begin(),exp_plan.end(),2) > 0) }; } public: - const integral_type power; gate_manifest_type(integral_type power) : power(power) {} std::uint32_t gates_amount() const override { @@ -131,7 +131,6 @@ namespace nil { static gate_manifest get_gate_manifest( std::size_t witness_amount, - std::size_t lookup_column_amount, integral_type power) { static gate_manifest manifest = gate_manifest(gate_manifest_type(power)); @@ -140,7 +139,7 @@ namespace nil { static manifest_type get_manifest() { static manifest_type manifest = manifest_type( - std::shared_ptr(new manifest_single_value_param(policy_type::arity)), // ?? + std::shared_ptr(new manifest_single_value_param(policy_type::arity)), false ); return manifest; @@ -160,7 +159,6 @@ namespace nil { static std::size_t get_rows_amount( std::size_t witness_amount, - std::size_t lookup_column_amount, integral_type power) { std::vector @@ -215,7 +213,7 @@ namespace nil { power(power), exp_plan(base4(power)), exp_precompute(get_precomputed_exps(exp_plan)), - rows_amount(get_rows_amount(this->witness_amount(), 0, power)) + rows_amount(get_rows_amount(this->witness_amount(), power)) { }; template @@ -228,7 +226,7 @@ namespace nil { power(power), exp_plan(base4(power)), exp_precompute(get_precomputed_exps(exp_plan)), - rows_amount(get_rows_amount(this->witness_amount(), 0, power)) + rows_amount(get_rows_amount(this->witness_amount(), power)) { }; mnt6_fp6_fixed_power( @@ -240,40 +238,46 @@ namespace nil { power(power), exp_plan(base4(power)), exp_precompute(get_precomputed_exps(exp_plan)), - rows_amount(get_rows_amount(this->witness_amount(), 0, power)) + rows_amount(get_rows_amount(this->witness_amount(), power)) { }; }; /* */ template - typename mnt6_fp6_fixed_power::result_type + using component_type = mnt6_fp6_fixed_power< + crypto3::zk::snark::plonk_constraint_system, + BlueprintFieldType>; + + template + typename component_type::result_type generate_assignments( - mnt6_fp6_fixed_power const& component, - assignment> const& assignment, - typename mnt6_fp6_fixed_power::input_type const& instance_input, + component_type const& component, + assignment> & assignment, + typename component_type::input_type const& instance_input, const std::uint32_t start_row_index) { using value_type = typename BlueprintFieldType::value_type; + using policy_type = typename component_type::policy_type; const std::vector exp_plan = component.exp_plan, exp_precompute = component.exp_precompute; - std::array::policy_type::arity> x; + std::array::policy_type::arity> x; for(std::size_t i = 0; i < x.size(); i++) { x[i] = var_value(assignment, instance_input.x[i]); } - using fp6_element = typename mnt6_fp6_fixed_power::fp6_element; - fp6_element X = fp6_element({ {x[0],x[1], x[2]}, {x[3],x[4]},x[5]}), Y = X; + using fp6_element = typename component_type::fp6_element; + fp6_element X = fp6_element({ {x[0],x[1],x[2]}, {x[3],x[4],x[5]} }), Y = X; std::size_t row = 0; auto fill_row = [&component, &assignment, &start_row_index, &row](fp6_element V) { - for(std::size_t i = 0; i < V.arity; i++) { + for(std::size_t i = 0; i < 6; i++) { assignment.witness(component.W(i),start_row_index + row) = V.data[i/3].data[i % 3]; } row++; @@ -295,17 +299,18 @@ namespace nil { } } - return typename mnt6_fp6_fixed_power::result_type( + return typename component_type::result_type( component, start_row_index); } template std::vector generate_gates( - mnt6_fp6_fixed_power const& component, + component_type const& component, circuit> & bp, assignment> const& assignment, - typename mnt6_fp6_fixed_power::input_type const& instance_input) + typename component_type::input_type const& instance_input) { + using var = typename component_type::var; const std::vector exp_plan = component.exp_plan, @@ -314,8 +319,8 @@ namespace nil { std::vector gate_list = {}; // at most 4 gate ids - using policy_type = typename mnt6_fp6_fixed_power::policy_type; - typename mnt6_fp6_fixed_power::fp4_constraint X, Y, Z, C; + using policy_type = typename component_type::policy_type; + typename component_type::fp6_constraint X, Y, Z, C; // power-4 gate for(std::size_t i = 0; i < policy_type::arity; i++) { @@ -324,7 +329,7 @@ namespace nil { } C = (X * X) * (X * X); - using constraint_type = typename mnt6_fp6_fixed_power::constraint_type; + using constraint_type = typename component_type::constraint_type; std::vector pow4_constrs = {}; for(std::size_t i = 0; i < policy_type::arity; i++) { @@ -378,19 +383,20 @@ namespace nil { } gate_list.push_back(bp.add_gate(square_constrs)); } + return gate_list; } template void generate_copy_constraints( - mnt6_fp6_fixed_power const& component, + component_type const& component, circuit> &bp, assignment> &assignment, - const typename mnt6_fp6_fixed_power::input_type &instance_input, + const typename component_type::input_type &instance_input, const std::size_t start_row_index) { - using var = typename mnt6_fp6_fixed_power::var; - using policy_type = typename mnt6_fp6_fixed_power::policy_type; + using var = typename component_type::var; + using policy_type = typename component_type::policy_type; const std::vector exp_plan = component.exp_plan, @@ -439,12 +445,12 @@ namespace nil { } template - typename mnt6_fp6_fixed_power::result_type + typename component_type::result_type generate_circuit( - mnt6_fp6_fixed_power const& component, + component_type const& component, circuit>& bp, assignment>& assignment, - typename mnt6_fp6_fixed_power::input_type const& instance_input, + typename component_type::input_type const& instance_input, const std::size_t start_row_index) { @@ -485,7 +491,7 @@ namespace nil { generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); - return typename mnt6_fp6_fixed_power::result_type( + return typename component_type::result_type( component, start_row_index); } } // namespace components diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp index 12336f795..76ed0c60f 100644 --- a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp @@ -27,7 +27,6 @@ #ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_EXPONENTIATION_HPP #define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_EXPONENTIATION_HPP -#include "nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp" #include #include #include @@ -41,6 +40,7 @@ #include #include +#include namespace nil { namespace blueprint { @@ -87,7 +87,8 @@ namespace nil { using manifest_type = plonk_component_manifest; using curve_type = nil::crypto3::algebra::curves::mnt4<298>; - using fixed_power_type = mnt4_fp4_fixed_power, BlueprintFieldType>; + using fixed_power_type = mnt4_fp4_fixed_power< + crypto3::zk::snark::plonk_constraint_system, BlueprintFieldType>; class gate_manifest_type : public component_gate_manifest { public: @@ -117,7 +118,9 @@ namespace nil { constexpr static std::size_t get_rows_amount( std::size_t witness_amount) { - return fixed_power_type::get_rows_amount(crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0) + 9 - 1; + return fixed_power_type::get_rows_amount(witness_amount, + crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0) + + 9 - 1; } constexpr static const std::size_t gates_amount = 7; diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.hpp new file mode 100644 index 000000000..90772c319 --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.hpp @@ -0,0 +1,492 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Circuit for final exponentiation for MNT6 elliptic curve pairings +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_EXPONENTIATION_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_EXPONENTIATION_HPP + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + // + // Curve E over prime field (F_p) as q points + // + // Pairing is possible due to q | p^k-1 + // + // This component raises element in F_{p^k} to power F = (p^k-1)/q + // + // For MNT6 curve k = 6 and F = (p^3 - 1)*(p + 1)*(p - w_0) + // + // The process of raising x to power F takes 6 stages: + // 1. Raise x to power p, x <- x^p (Frobenius map) + // 2. Repeat: x <- x^p, now x holds x^(p^2) + // 3. Repeat: x <- x^p, now x holds x^(p^3) + // 4. Divide by initial value x, now x holds x^(p^3-1), save to x' + // 5. Raise x' to power p: x1 <- x'^p = x^((p^3-1)*p) + // 6. Multiply x1 by x', now x1 holds x^((p^3-1)*(p+1)), save to x'' + // 7. Raise x'' to power p: x2 <- x''^p, x2 = x^((p^3-1)*(p+1)*p) + // 8. Raise x'' to power w_0: x3 <- x''^w_0, x3 = x^((p^3-1)*(p+1)*w_0), done with sub-circuit + // 9. Final result: inverse division x2 * x3^-1 + // + // Circuit requires 6 witnesses, 6 inputs and 6 outputs + // 6 gates are used: + // Gate 0: Raising to power p, "Frobenius map" + // Gate 1: "Division in Fp6" : x_next = x_prev / x + // Gate 2: "Inverse Division in Fp6": x_next = x / x_prev + // Gate 3: "Multiplication" + // Gate 4: "Squaring" + // Gate 5: "Cubing" + // Gate 6: "Fourth power" + // Gates 3-5 are used for powering to w_0 + using namespace detail; + + template + class mnt6_exponentiation; + + template + class mnt6_exponentiation> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + using curve_type = nil::crypto3::algebra::curves::mnt6<298>; + + using fixed_power_type = mnt6_fp6_fixed_power< + crypto3::zk::snark::plonk_constraint_system, BlueprintFieldType>; + + class gate_manifest_type : public component_gate_manifest { + public: + gate_manifest_type() {} + + std::uint32_t gates_amount() const override { + return mnt6_exponentiation::gates_amount; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount) + { + static gate_manifest manifest = gate_manifest(gate_manifest_type()) + .merge_with(fixed_power_type::get_gate_manifest(witness_amount, crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0)); + return manifest; + } + + static manifest_type get_manifest() { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(4)), + false + ); + + return manifest; + } + + constexpr static std::size_t get_rows_amount( + std::size_t witness_amount) + { + auto x = fixed_power_type::get_rows_amount( + witness_amount, + crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0) + + 12; + std::cout << "Get rows: " << x << std::endl; + return x; + } + + constexpr static const std::size_t gates_amount = 3; + const std::size_t rows_amount = get_rows_amount(0); + + struct input_type { + std::array x; + + std::vector> all_vars() { + return {x[0], x[1], x[2], x[3], x[4], x[5]}; + } + }; + + struct result_type { + std::array output; + + result_type(const mnt6_exponentiation &component, std::uint32_t start_row_index) { + std::size_t last_row = start_row_index + component.rows_amount - 1; + for(std::size_t i = 0; i < 6; i++) { + output[i] = var(component.W(i), last_row, false, var::column_type::witness); + } + } + + std::vector> all_vars() { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template + explicit mnt6_exponentiation(ContainerType witness) : + component_type(witness, {}, {}, get_manifest()) {}; + + template + mnt6_exponentiation( + WitnessContainerType witness, + ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + mnt6_exponentiation( + std::initializer_list + witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, unsigned long long T_) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_mnt6_exponentiation = + mnt6_exponentiation>; + + template + typename plonk_mnt6_exponentiation::result_type + generate_assignments( + plonk_mnt6_exponentiation const& component, + assignment> &assignment, + typename plonk_mnt6_exponentiation::input_type const& instance_input, + const std::uint32_t start_row_index) + { + using component_type = plonk_mnt6_exponentiation; + using var = typename component_type::var; + using value_type = typename BlueprintFieldType::value_type; + using curve_type = nil::crypto3::algebra::curves::mnt6<298>; + + using fixed_power_type = mnt6_fp6_fixed_power< + crypto3::zk::snark::plonk_constraint_system, BlueprintFieldType>; + + fixed_power_type fixed_power_instance( + component._W, component._C, component._PI, + crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0); + + std::array x; + + for(std::size_t i = 0; i < x.size(); i++) { + x[i] = var_value(assignment, instance_input.x[i]); + } + + using policy_type_fp6 = crypto3::algebra::fields::fp6_2over3; + using fp6_element = typename policy_type_fp6::value_type; + + fp6_element + input = fp6_element({ {x[0], x[1], x[2]}, {x[3], x[4], x[5]} }), + elt = input; + + std::size_t row = start_row_index; + + auto fill_row = [&assignment, &component, &row](fp6_element const& V) + { + for(std::size_t i = 0; i < 6; ++i) { + assignment.witness(component.W(i),row) = V.data[i/3].data[i%3]; + } + ++row; + }; + + // 0: Initial value + fill_row(elt); + + // 1: elt <- elt^p + elt = elt.Frobenius_map(1); + fill_row(elt); + + // 2: elt <- (elt^p), now elt holds= x^(p^2) + elt = elt.Frobenius_map(1); + fill_row(elt); + + // 3: elt <- (elt^p), now elt holds= x^(p^3) + elt = elt.Frobenius_map(1); + fill_row(elt); + + // 4: elt <- elt/x, elt now holds x^(p^3-1) + fill_row(input); + elt = elt*input.inversed(); + // 5: + fill_row(elt); + + // 6: elt2 <- (elt^p), now elt2 holds x^((p^3-1)*p) + fp6_element elt2 = elt.Frobenius_map(1); + fill_row(elt2); + + // 7: elt2 <- elt2*elt, now elt2 holds x^((p^3-1)*(p+1)) + elt2 = elt2*elt; + fill_row(elt2); + + // 8: elt <- (elt2^p), now elt holds x^((p^3-1)*(p+1)*p) + elt = elt2.Frobenius_map(1); + fill_row(elt); + + /* Fill rows for raising elt2 = x^((p^3-1)*(p+1)) to power w0 */ + + // The input is from 7th row + std::array transfer_vars = { + var(component.W(0), start_row_index + 7, false), + var(component.W(1), start_row_index + 7, false), + var(component.W(2), start_row_index + 7, false), + var(component.W(3), start_row_index + 7, false), + var(component.W(4), start_row_index + 7, false), + var(component.W(5), start_row_index + 7, false), + }; + + typename fixed_power_type::input_type pow_input = { transfer_vars }; + typename fixed_power_type::result_type pow_output = + generate_assignments(fixed_power_instance, assignment, pow_input, row); + row += fixed_power_instance.rows_amount; + + fp6_element elt3({ + { + var_value(assignment, pow_output.output[0]), + var_value(assignment, pow_output.output[1]), + var_value(assignment, pow_output.output[2]), + } , { + var_value(assignment, pow_output.output[3]), + var_value(assignment, pow_output.output[4]), + var_value(assignment, pow_output.output[5]) + } + }); + // Now elt3 holds x^((p^3-1)*(p+1)*w0) + // Do not fill as sub-circuit output results are on the last row + // fill_row(elt3); + + // 8+R-1: Final result is elt/elt3 = x^((p^3-1)*(p+1)*p) * x^(-(p^3-1)(p+1)*w0) = x^((p^3-1)*(p+1)*(p-w0)) + fill_row(elt); + fill_row(elt3); + elt = elt*elt3.inversed(); + // 10+R-1 + fill_row(elt); + + return typename plonk_mnt6_exponentiation::result_type( + component, start_row_index); + } + + template + std::vector + generate_gates( + plonk_mnt6_exponentiation const& component, + circuit> &bp, + assignment> &assignment, + typename plonk_mnt6_exponentiation::input_type const& instance_input) + { + using var = typename plonk_mnt6_exponentiation::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using fp6_constraint = detail::abstract_fp6_element; + + std::vector gate_list = {}; + + std::vector constrs = {}; + + fp6_constraint X, Xn, Xp, R; + + for(std::size_t i = 0; i < 6; ++i) { + X[i] = var(component.W(i), 0, true); + Xn[i] = var(component.W(i), 1, true); + Xp[i] = var(component.W(i), -1, true); + } + + /* Frobenius gate - 0 + * Ensures x_next = x^p = x.frobenius_map(1) + */ + { + using fp6_ep = typename crypto3::algebra::fields::fp6_2over3::extension_policy; + using fp3_ep = typename crypto3::algebra::fields::fp3::extension_policy; + + typename BlueprintFieldType::value_type fc3[] = { + 1, + fp3_ep::Frobenius_coeffs_c1[1], + fp3_ep::Frobenius_coeffs_c2[1] + }; + + typename BlueprintFieldType::value_type fc6[] = { + 1, + fp6_ep::Frobenius_coeffs_c1[1], + }; + + constrs.clear(); + for(std::size_t i = 0; i < 6; ++i) { + constraint_type coeff = constraint_type() + fc6[i/3]*fc3[i%3]; + constrs.push_back(Xn[i] - coeff*X[i]); + } + + gate_list.push_back(bp.add_gate(constrs)); + } + + /* Division gate - 1 + * Ensures x_next = x_prev/x : x_next * x - x_prev = 0 + */ + { + R = Xn*X - Xp; + + constrs.clear(); + for(std::size_t i = 0; i < 6; ++i) { + constrs.push_back(R[i]); + } + + gate_list.push_back(bp.add_gate(constrs)); + } + + /* Multiplication gate - 2 + * Ensures x_next = x*x_prev: x_next - x*x_prev = 0 + */ + { + R = Xn - X*Xp; + + constrs.clear(); + for(std::size_t i = 0; i < 6; ++i) { + constrs.push_back(R[i]); + } + + gate_list.push_back(bp.add_gate(constrs)); + } + + return gate_list; + } + + template + void generate_copy_constraints( + plonk_mnt6_exponentiation const& component, + circuit> &bp, + assignment> &assignment, + const typename plonk_mnt6_exponentiation::input_type &instance_input, + const std::size_t start_row_index) + { + using component_type = plonk_mnt6_exponentiation; + using var = typename plonk_mnt6_exponentiation::var; + + using fixed_power_type = typename component_type::fixed_power_type; + using curve_type = nil::crypto3::algebra::curves::mnt6<298>; + fixed_power_type power_instance( component._W, component._C, component._PI, + crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0); + std::size_t R = power_instance.rows_amount; + + // initial data in row 0 + for(std::size_t i = 0; i < 6; ++i) { + bp.add_copy_constraint({var(component.W(i), start_row_index, false), instance_input.x[i]}); + } + + // initial data in row 4 + for(std::size_t i = 0; i < 6; ++i) { + bp.add_copy_constraint({var(component.W(i), start_row_index + 4, false), instance_input.x[i]}); + } + + // Copy from 8 row to R+9 row + for(std::size_t i = 0; i < 6; ++i) { + bp.add_copy_constraint({ + var(component.W(i), start_row_index + R + 9, false), + var(component.W(i), start_row_index + 8, false), + }); + } + + // Copy from R+8 row to R+10 row + for(std::size_t i = 0; i < 6; ++i) { + bp.add_copy_constraint({ + var(component.W(i), start_row_index + R + 10, false), + var(component.W(i), start_row_index + R + 8, false), + }); + } + } + + template + typename plonk_mnt6_exponentiation::result_type + generate_circuit( + plonk_mnt6_exponentiation const& component, + circuit> &bp, + assignment> &assignment, + const typename plonk_mnt6_exponentiation::input_type &instance_input, + const std::size_t start_row_index) + { + using component_type = plonk_mnt6_exponentiation; + using var = typename component_type::var; + using fixed_power_type = typename component_type::fixed_power_type; + using curve_type = nil::crypto3::algebra::curves::mnt6<298>; + + fixed_power_type power_instance( component._W, component._C, component._PI, + crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0); + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + + // Frobenius gates + assignment.enable_selector(selector_index[0], start_row_index + 0); + assignment.enable_selector(selector_index[0], start_row_index + 1); + assignment.enable_selector(selector_index[0], start_row_index + 2); + // Copy at 3 + // Division gate at 4 + assignment.enable_selector(selector_index[1], start_row_index + 4); + // Frobenius at 5 + assignment.enable_selector(selector_index[0], start_row_index + 5); + // Multiplication at 6 + assignment.enable_selector(selector_index[2], start_row_index + 6); + // Frobenius at 7 + assignment.enable_selector(selector_index[0], start_row_index + 7); + + // Power to w0 sub-circuit takes input from 7-th rouw + std::array power_input_vars; + for(std::size_t i = 0 ; i < 6; ++i ) { + power_input_vars[i] = var(component.W(i), start_row_index + 7, false); + } + + typename fixed_power_type::input_type power_input = { power_input_vars }; + typename fixed_power_type::result_type power_output = + generate_circuit(power_instance, bp, assignment, power_input, start_row_index + 9); + std::size_t R = power_instance.rows_amount; + + // expect result at start_rows_index + R + 9 + + // Inverse division gate + assignment.enable_selector(selector_index[1], start_row_index + R + 10); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + + return typename plonk_mnt6_exponentiation::result_type( + component, start_row_index); + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_mnt6_exponentiation_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bab8a9b70..6129519ef 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -81,6 +81,7 @@ set(NON_NATIVE_TESTS_FILES "algebra/fields/plonk/non_native/equality_flag" "algebra/fields/plonk/non_native/division_remainder" "algebra/fields/plonk/non_native/mnt4_fp4_fixed_power" + "algebra/fields/plonk/non_native/mnt6_fp6_fixed_power" "non_native/plonk/bool_scalar_multiplication" "non_native/plonk/add_mul_zkllvm_compatible" "non_native/plonk/scalar_non_native_range" @@ -191,6 +192,7 @@ set(HASHES_TESTS_FILES set(PAIRING_TESTS_FILES "algebra/pairing/weierstrass/plonk/mnt4_pairing" "algebra/pairing/weierstrass/plonk/mnt4_exponentiation" + "algebra/pairing/weierstrass/plonk/mnt6_exponentiation" "algebra/pairing/weierstrass/r1cs/miller_loop" "algebra/pairing/weierstrass/r1cs/precomputation") diff --git a/test/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.cpp b/test/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.cpp index 536620f2b..4275238cb 100644 --- a/test/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.cpp +++ b/test/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.cpp @@ -49,7 +49,7 @@ using namespace nil; template void test_mnt4_fp4_fixed_power( std::vector public_input, - typename CurveType::base_field_type::extended_integral_type power, + typename CurveType::base_field_type::integral_type power, typename CurveType::gt_type::value_type expected_res) { using curve_type = CurveType; @@ -141,8 +141,8 @@ BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt4_fp4_fixed_power_test) { ), }; - //auto fixed_power = pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0; - auto fixed_power = pairing::detail::pairing_params::final_exponent; + auto fixed_power = pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0; +// auto fixed_power = pairing::detail::pairing_params::final_exponent; // auto fixed_power = integral_type("100"); for(std::size_t i = 0; i < test_gt_elems.size(); i++) { diff --git a/test/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.cpp b/test/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.cpp new file mode 100644 index 000000000..bd2a6e726 --- /dev/null +++ b/test/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.cpp @@ -0,0 +1,174 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// +// exponentiation to a fixed power in Fp4 for mnt6 GT +// +#define BOOST_TEST_MODULE blueprint_plonk_mnt6_fp4_fixed_power_test + +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; + +template +void test_mnt6_fp6_fixed_power( + std::vector public_input, + typename CurveType::base_field_type::integral_type power, + typename CurveType::gt_type::value_type expected_res) +{ + using curve_type = CurveType; + using BlueprintFieldType = typename curve_type::base_field_type; + + constexpr std::size_t WitnessColumns = WitnessAmount; + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 0; + constexpr std::size_t SelectorColumns = 5; + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using AssignmentType = blueprint::assignment; + using hash_type = crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::mnt6_fp6_fixed_power; + + typename component_type::input_type instance_input = { + var(0, 0, false, var::column_type::public_input), + var(0, 1, false, var::column_type::public_input), + var(0, 2, false, var::column_type::public_input), + var(0, 3, false, var::column_type::public_input), + var(0, 4, false, var::column_type::public_input), + var(0, 5, false, var::column_type::public_input) + }; + + auto result_check = [&expected_res, public_input](AssignmentType &assignment, + typename component_type::result_type &real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "GT (mnt6_FP6) fixed power test: " << "\n"; + std::cout << "input : " << public_input[0].data << "," << public_input[1].data << "\n"; + std::cout << "input : " << public_input[2].data << "," << public_input[3].data << "\n"; + std::cout << "input : " << public_input[4].data << "," << public_input[5].data << "\n"; + std::cout << "expected: " << expected_res.data[0].data[0] << "," << expected_res.data[0].data[1] << ",\n"; + std::cout << " : " << expected_res.data[0].data[2] << "," << expected_res.data[1].data[0] << ",\n"; + std::cout << " : " << expected_res.data[1].data[1] << "," << expected_res.data[1].data[2] << ",\n"; + std::cout << "real : " << var_value(assignment, real_res.output[0]).data << "," << var_value(assignment, real_res.output[1]).data << ",\n"; + std::cout << " " << var_value(assignment, real_res.output[2]).data << "," << var_value(assignment, real_res.output[3]).data << "\n\n"; + std::cout << " " << var_value(assignment, real_res.output[4]).data << "," << var_value(assignment, real_res.output[5]).data << "\n\n"; + #endif + assert(expected_res.data[0].data[0] == var_value(assignment, real_res.output[0])); + assert(expected_res.data[0].data[1] == var_value(assignment, real_res.output[1])); + assert(expected_res.data[0].data[2] == var_value(assignment, real_res.output[2])); + assert(expected_res.data[1].data[0] == var_value(assignment, real_res.output[3])); + assert(expected_res.data[1].data[1] == var_value(assignment, real_res.output[4])); + assert(expected_res.data[1].data[2] == var_value(assignment, real_res.output[5])); + return true; + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance(witnesses, + std::array{}, + std::array{}, + power); + + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::blueprint::connectedness_check_type::type::STRONG, + power); +} + +constexpr static const std::size_t random_tests_amount = 10; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt6_fp4_fixed_power_test) { + using curve_type = crypto3::algebra::curves::mnt6_298; + using group_type = typename curve_type::gt_type; + using base_field_value = curve_type::base_field_type::value_type; + + typedef typename group_type::value_type group_value_type; + typedef typename group_type::underlying_field_type::value_type underlying_field_type; + typedef typename group_type::base_field_type::value_type field_value_type; + typedef typename group_type::base_field_type::integral_type integral_type; + + std::vector test_gt_elems = { + group_value_type( + underlying_field_type( + 3,2,1 +// 0x22c26a3c19d56fc8790485554be5dc4351961a5162c3634965dc8ae56701157e_cppui254, +// 0x1e3305b98bf381650491b7b63559d20d662b70f1616e680a19170715b59a3426_cppui254 + ), + underlying_field_type( + 4,5,6 +// 0x148a1f438a4cd0d807549cb7f9ec9f41dba3d8b14a6b0f2489d9b9f626d6fd31_cppui254, +// 0x3cc907ef65b0eff91d027e4771e9116a0b125325627b6bdf55037702220b1b2_cppui254 + ) + ), + }; + + auto fixed_power = pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0; +// auto fixed_power = pairing::detail::pairing_params::final_exponent; +// auto fixed_power = integral_type("100"); + + for(std::size_t i = 0; i < test_gt_elems.size(); i++) { + std::cout << "Test instance # " << (i+1) << "\n"; + group_value_type P = test_gt_elems[i]; + group_value_type R = P.pow(fixed_power); + + test_mnt6_fp6_fixed_power( + std::vector{ + P.data[0].data[0], + P.data[0].data[1], + P.data[0].data[2], + P.data[1].data[0], + P.data[1].data[1], + P.data[1].data[2], + }, + fixed_power, + R); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.cpp b/test/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.cpp new file mode 100644 index 000000000..5abeeb2f6 --- /dev/null +++ b/test/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.cpp @@ -0,0 +1,152 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_pairing_mnt6_298 + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#include "../../../../test_plonk_component.hpp" + +using namespace nil; +using namespace blueprint::components::detail; + +template +void test_mnt6_298_exponentiation(std::vector public_input, + std::vector expected_res) { + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = 8; + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment; + + using value_type = typename FieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::mnt6_exponentiation; + + typename component_type::input_type instance_input = { + var(0,0, false, var::column_type::public_input), // f[0] + var(0,1, false, var::column_type::public_input), // f[1] + var(0,2, false, var::column_type::public_input), // f[2] + var(0,3, false, var::column_type::public_input), // f[3] + var(0,4, false, var::column_type::public_input), // f[3] + var(0,5, false, var::column_type::public_input), // f[3] + }; + + auto result_check = [&expected_res](AssignmentType const& assignment, + typename component_type::result_type const& real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "mnt6-298 Final exponentiation: expected res VS output\n"; + for(std::size_t i = 0; i < 6; 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 < 6; i++) { + assert(expected_res[i] == var_value(assignment, real_res.output[i])); + } + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance(witnesses, // witnesses + std::array{}, // constants + std::array{} // public inputs + ); + + nil::crypto3::test_component ( + component_instance, desc, public_input, result_check, instance_input); +} + +static const std::size_t random_tests_amount = 5; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt6_pairing_test) { + using curve_type = crypto3::algebra::curves::mnt6_298; + using gt_group_type = typename curve_type::gt_type; + using base_field_value = curve_type::base_field_type::value_type; + using field_type = typename curve_type::gt_type::base_field_type; + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + std::vector + AB_ML = { + 0x02f487892d5db56458de095daa86a1ebb90a07517cccad41a46b48438ca24173be6f3a32c6cb_cppui298, + 0x01217e7c9d35c9c75d1fbcb5d46e482d053c9da4ab9f3c37b741900af9ca42ff9b50001aec5f_cppui298, + 0x01f0324b79887ef191ff8d0c9152cfed7445abb63faa598eaa51ddfea0af7a642f7cf6ba70fe_cppui298, + 0x033f650fdb078d7fb4a8153c8c621d84dd01460a4173bd01371d360e71c6cb46392dd0e00f1f_cppui298, + 0x0023d3795211bb71c579250426dc77d6c7a32a24136b0f02780be3b2f4c674b54a6a5989f9f4_cppui298, + 0x01b5c328e47a03bddb00f0992ddbd9945af3e0f4bb8244d29b9b24ca2390e92ecc5666a76191_cppui298 + }, + AB_FE = { + /* + 0x15dec519566f20aa6a3bd4ddb0cee93f9ea157499f20273124883934daeea1867ea53ac4b9b_cppui298, + 0x3b5fc396c41a54c7c661b3d34ead6f284a50886534f036c004eb31d8d7cb061d3419f6a9d58_cppui298, + 0x1b79ac259f41bfdc8aa623c65075d0a0fb1823467b174c4ac82cada904d96f3640fd491baf7_cppui298, + 0x667fef1c7f96557416d9728951128edb921b4ecad5592fd898db5867bf40dfa20de956a717_cppui298, + 0x39143f4b1cee4646b2a1a77d27fc21aead95a0922a4a5f9facb82d88120b01737fafb1f1d8d_cppui298, + 0x2f84e7545b29729839b5871c25a131756558cd8bba38d2ef7439f20b5cb54be2a271a3c1bba_cppui298, + */ + 0x025aba200efbefa81017a858457abfc1e83ce8f7e92788e414fe90bacd465395dec8463bf09a_cppui298, + 0x002ca86c9adeb0cfd62db143cc3fc5b6adb9fd09419a43b6c5b841f129a0ef71fa3881a4b743_cppui298, + 0x00e7bc013d484cc770ddcaa994422f153265c143a64549b916f6893c2e2ab5458e7c3ea5f3d7_cppui298, + 0x01b26a752dd48454cc5361994c65bbea4f5315383c0051be7285afebd49608614a17945879ff_cppui298, + 0x0378d4dd54964822cabd59be83661224fb84a4820c190c62d3a701e2eaf60ac8dc13e0db1c99_cppui298, + 0x037f4a31f3bc24e9876d57a5b224d8f6475d36407e092bc03144d1bf042a0aee471639db2439_cppui298 + }; + + std::cout << "mnt6-298 Final exponentiation test\n"; + test_mnt6_298_exponentiation(AB_ML, AB_FE); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/test_plonk_component.hpp b/test/test_plonk_component.hpp index d7d7de74e..73498e9fc 100644 --- a/test/test_plonk_component.hpp +++ b/test/test_plonk_component.hpp @@ -252,7 +252,7 @@ namespace nil { variable.get() = assignment.get_batch_variable_map().at(variable); } } -/* +#if 0 std::ofstream fass("circuit.tbl"); assignment.export_table(fass); fass.close(); @@ -260,7 +260,7 @@ namespace nil { std::ofstream fcirc("circuit.crt"); bp.export_circuit(fcirc); fcirc.close(); -*/ +#endif result_check(assignment, component_result); if constexpr (!PrivateInput) { @@ -317,12 +317,12 @@ namespace nil { } desc.rows_amount = zk::snark::basic_padding(assignment); -//#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED +#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED std::cout << "Usable rows: " << desc.usable_rows_amount << std::endl; std::cout << "Padded rows: " << desc.rows_amount << std::endl; // profiling(assignment); -//#endif +#endif //assignment.export_table(std::cout); //bp.export_circuit(std::cout); From ea0388c72cf0a8704f196a97bde9c4264e5471d7 Mon Sep 17 00:00:00 2001 From: Vasiliy Olekhov Date: Fri, 26 Apr 2024 17:44:24 +0300 Subject: [PATCH 5/7] Updates for review, working MNT6 miller loop #355 --- .../plonk/non_native/detail/abstract_fp3.hpp | 4 +- .../weierstrass/plonk/mnt6_miller_loop.hpp | 813 ++++++++++++++++++ test/CMakeLists.txt | 3 +- ...{mnt4_pairing.cpp => mnt4_miller_loop.cpp} | 4 +- .../weierstrass/plonk/mnt6_miller_loop.cpp | 158 ++++ test/test_plonk_component.hpp | 2 +- 6 files changed, 979 insertions(+), 5 deletions(-) create mode 100644 include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.hpp rename test/algebra/pairing/weierstrass/plonk/{mnt4_pairing.cpp => mnt4_miller_loop.cpp} (98%) create mode 100644 test/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.cpp diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp3.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp3.hpp index 2b191b914..e7086e420 100644 --- a/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp3.hpp +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp3.hpp @@ -29,6 +29,7 @@ #define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP3_HPP #include +#include namespace nil { namespace blueprint { @@ -37,6 +38,7 @@ namespace nil { template class abstract_fp3_element { public: + using policy_type_fp3 = crypto3::algebra::fields::fp3; std::array data; T& operator[](std::size_t idx) { @@ -47,7 +49,7 @@ namespace nil { } constexpr abstract_fp3_element operator*(abstract_fp3_element const& other) { - auto s = UnderlyingFieldType::non_residue; + auto s = policy_type_fp3::extension_policy::non_residue; return { data[0]*other[0] + s*(data[1]*other[2] + data[2]*other[1]), data[0]*other[1] + data[1]*other[0] + s*data[2]*other[2], diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.hpp new file mode 100644 index 000000000..c9b1c8132 --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.hpp @@ -0,0 +1,813 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of Miller loop component for MNT6 pairings +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_MILLER_LOOP_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_MILLER_LOOP_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + template + std::vector base(T x) { + std::vector res = {(std::uint8_t)(x % B)}; + if (x > 0) { + x /= B; + while (x > 0) { + res.insert(res.begin(), std::uint8_t(x % B)); + x /= B; + } + } + return res; + } + } // namespace detail + // + using mnt6_g2_params = crypto3::algebra::curves::detail:: + mnt6_g2_params<298,crypto3::algebra::curves::forms::short_weierstrass>; + + using mnt6_pairing_params = crypto3::algebra::pairing::detail:: + pairing_params; + + // + // Component for computing the result of applying the Miller loop for mnt6 curve + // to two points P from E(F_p) and Q from E'(F_p^3). + // Input: P[2], Q[6] ( we assume P and Q are NOT (0,0), i.e. not the points at infinity, NOT CHECKED ) + // Output: f[6]: an element of F_p^6 + // + // Each iteration of the Miller loop adds "doubling" row to the circuit: + // + // f0 f1 f2 f3 f4 f5 P0 P1 T0 T1 T2 T3 T4 T5 Q0 Q1 Q2 Q3 Q4 Q5 L0 L1 L2 L3 L4 L5 + // Gate 0: "doubling" + // Constraints: + // 0. UT = untwisted T + // 1. L = (3*UT.x^2+a)/(2*UT.y) + // 2. f_next = f*f*(L*(P.x-UT.x) - (P.y - UT.y)) + // 3. T_next = T + T + // + // If current iteration needs to add addition, then "addition" row is inserted: + // f0 f1 f2 f3 f4 f5 P0 P1 T0 T1 T2 T3 T4 T5 Q0 Q1 Q2 Q3 Q4 Q5 L0 L1 L2 L3 L4 L5 + // Gate 1: "addition" + // Constraints: + // 0. UT = untwisted T, UQ = untwisted Q + // 1. L = (UT.y - UQ.y) / (UT.x - UQ.x) + // 2. f_next = f*(L*(P.x - UT.x) - (P.y - UT.y)) + // 3. T_next = T + Q + // + // 219 rows total: 147 doubling and 71 addition + 1 row with result + // P is copied in addition and doubling rows of the circuit + // Q is copied only in addition rows. + // Initial value f (1,0,0,0,0) is copied from constants column + // + // Total number of copy constraints: 724 = 4+2*147+6*71 + // + // We can reduce number of copy constraints by next trick: + // 1. Copy Q in doubling rows too + // 2. To each gate (doubling and addition) add addition 6 constraints: + // w_i = w_i_rot(1), i = 6,7 (P), 14..19 (Q) + // 3. Leave copy constraints for P and Q on the first row + // Total number of copy constraints will be: + // 4+2+6 = 12 //xz + // At the expense of adding 6 additional constraints to each gate + // + // Witnesses for L0 and L1, L2 could be removed as they are always zero + + using namespace detail; + using detail::base; + + template + class mnt6_miller_loop; + + template + class mnt6_miller_loop> + : public plonk_component { + + public: + using component_type = plonk_component; + using integral_type = typename BlueprintFieldType::integral_type; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + + class gate_manifest_type : public component_gate_manifest { + public: + std::size_t witness_amount; + + gate_manifest_type(std::size_t witness_amount_) : witness_amount(witness_amount_) {} + + std::uint32_t gates_amount() const override { + return mnt6_miller_loop::gates_amount; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t witness_amount) + { + gate_manifest manifest = + gate_manifest(gate_manifest_type(witness_amount)); + return manifest; + } + + static manifest_type get_manifest() + { + static manifest_type manifest = manifest_type( + std::shared_ptr(new manifest_single_value_param(18)), + true // constant column required + ); + return manifest; + } + + static std::size_t get_rows_amount( + std::size_t witness_amount) + { + std::vector C_bin = base<2>(C_val); + + std::size_t result = 0; + + // doubling blocks x LineFunctions + result += C_bin.size()-1; + // adding blocks x LineFunctions + result += std::count(C_bin.begin(),C_bin.end(), 1) - 1; + // final result (for gate uniformity) + result += 1; + return result; + } + + constexpr static integral_type C_val = mnt6_pairing_params::ate_loop_count; + std::vector C_bin = base<2>(C_val); + + constexpr static const std::size_t gates_amount = 2; + const std::size_t rows_amount = get_rows_amount(this->witness_amount()); + + struct input_type { + std::array P; + std::array Q; + + std::vector> all_vars() { + return {P[0], P[1], Q[0], Q[1], Q[2], Q[3], Q[4], Q[5]}; + } + }; + + struct result_type { + std::array output; + + result_type(mnt6_miller_loop const& component, std::uint32_t start_row_index) { + std::size_t res_row = start_row_index + component.rows_amount - 1; + for(std::size_t i = 0; i < 6; i++) { + output[i] = var(component.W(i), res_row, false, var::column_type::witness); + } + } + + std::vector> all_vars() { + std::vector> res = {}; + + for(auto & e : output) { res.push_back(e); } + return res; + } + }; + + template + explicit mnt6_miller_loop(ContainerType witness) : + component_type(witness, {}, {}, get_manifest()) {}; + + template + mnt6_miller_loop( + WitnessContainerType witness, + ConstantContainerType constant, + PublicInputContainerType public_input) : + component_type(witness, constant, public_input, get_manifest()) {}; + + mnt6_miller_loop( + std::initializer_list witnesses, + std::initializer_list constants, + std::initializer_list public_inputs) : + component_type(witnesses, constants, public_inputs, get_manifest()) {}; + }; + + template + using plonk_miller_loop = + mnt6_miller_loop>; + + template + typename plonk_miller_loop::result_type + generate_assignments( + plonk_miller_loop const& component, + assignment> &assignment, + typename plonk_miller_loop::input_type const& instance_input, + const std::uint32_t start_row_index) + { + using component_type = plonk_miller_loop; + using var = typename component_type::var; + using value_type = typename BlueprintFieldType::value_type; + + using policy_type_fp3 = crypto3::algebra::fields::fp3; + using fp3_element = typename policy_type_fp3::value_type; + using curve_point = std::array; + + using curve_type = nil::crypto3::algebra::curves::mnt6<298>; + + value_type + xP = var_value(assignment, instance_input.P[0]), + yP = var_value(assignment, instance_input.P[1]); + + std::array + xQ = { + var_value(assignment, instance_input.Q[0]), + var_value(assignment, instance_input.Q[1]), + var_value(assignment, instance_input.Q[2]), + }, + yQ = { + var_value(assignment, instance_input.Q[3]), + var_value(assignment, instance_input.Q[4]), + var_value(assignment, instance_input.Q[5]) + }; + + curve_point Q = { fp3_element(xQ[0], xQ[1], xQ[2]), fp3_element(yQ[0], yQ[1], yQ[2])}; + + /* Calculate point doubling on E', affine coordinates */ + auto double_point = [](curve_point const& T) { + fp3_element a(curve_type::g2_type<>::params_type::a), + lambda = (3*T[0].pow(2) + a) * (2*T[1]).inversed(), + xR = lambda.pow(2) - 2*T[0], + yR = (3*T[0])*lambda - lambda.pow(3) - T[1]; + return curve_point({xR, yR}); + }; + + /* Calculate point addition on E', affine coordinates */ + auto add_points = [](curve_point const& T, curve_point const& Q) { + fp3_element + lambda = (T[1] - Q[1])*(T[0] - Q[0]).inversed(), + xR = lambda*lambda - T[0] - Q[0], + yR = (2*T[0] + Q[0])*lambda - lambda.pow(3) - T[1]; + return curve_point({xR, yR}); + }; + + using policy_type_fp6 = crypto3::algebra::fields::fp6_2over3; + using fp6_element = typename policy_type_fp6::value_type; + + auto insert_row_doubling = [ + &double_point, &assignment, &component, &start_row_index, &xP, &yP] + (fp6_element const& f, curve_point& T, std::size_t row) + { + fp6_element x, y, x1, y1, g, three({{3,0,0}, {0,0,0}}); + + x = fp6_element({ {xP, 0, 0}, {0,0,0} }); + y = fp6_element({ {yP, 0, 0}, {0,0,0} }); + + // Untwisting: E'/Fp3 -> E/Fp6 + // x * u^-1, y * (uv)^-1 + // mnt6 nr = 5, + // u = (0,1,0), v = ((0,0,0), (1,0,0)) + // u^3 = nr, v^2 = u + // u = ((0,1,0),(0,0,0)), u^-1 = ((0, 0, 1/nr), (0, 0, 0)) + // v = ((0,0,0),(1,0,0)), (uv)^-1 = ((0, 0, 0), (0, 1/nr, 0)) + // + // nri = nr^-1 + // ((x0,x1,x2),(x3,x4,x5)) * (u)^-1 = ( (x1, x2, x0*nri), (x4, x5, x3*nri) ) + // ((x0,x1,x2),(x3,x4,x5)) * (uv)^-1 = ( (x4, x5, x3*nri), (x2, x0*nri, x1*nri) ) + // + // T is in form (X,Y)/Fp3: + // X: ((x0,x1,x2),(0,0,0)) * (u)^-1 = ( (x1, x2, x0*nri), (0, 0, 0) ) + // Y: ((y0,y1,y2),(0,0,0)) * (uv)^-1 = ( (0, 0, 0), (y2, y0*nri, y1*nri) ) + // + value_type nri = policy_type_fp3::extension_policy::non_residue.inversed(); + + x1 = fp6_element({ {T[0].data[1], T[0].data[2], T[0].data[0]*nri }, {0,0, 0} }); + y1 = fp6_element({ {0,0,0}, {T[1].data[2], T[1].data[0]*nri, T[1].data[1]*nri}}); + + fp6_element a({{curve_type::g1_type<>::params_type::a,0,0},{0,0,0}}); + + fp6_element lf =(three*x1.pow(2) + a)*(y1+y1).inversed(); + g = f.pow(2) * (lf*(x-x1) + (y1-y)); + + // f + assignment.witness(component.W(0),start_row_index + row) = f.data[0].data[0]; + assignment.witness(component.W(1),start_row_index + row) = f.data[0].data[1]; + assignment.witness(component.W(2),start_row_index + row) = f.data[0].data[2]; + assignment.witness(component.W(3),start_row_index + row) = f.data[1].data[0]; + assignment.witness(component.W(4),start_row_index + row) = f.data[1].data[1]; + assignment.witness(component.W(5),start_row_index + row) = f.data[1].data[2]; + + // P + assignment.witness(component.W(6),start_row_index + row ) = xP; + assignment.witness(component.W(7),start_row_index + row ) = yP; + + // T <- T+T + assignment.witness(component.W(8), start_row_index + row) = T[0].data[0]; + assignment.witness(component.W(9), start_row_index + row) = T[0].data[1]; + assignment.witness(component.W(10),start_row_index + row) = T[0].data[2]; + assignment.witness(component.W(11),start_row_index + row) = T[1].data[0]; + assignment.witness(component.W(12),start_row_index + row) = T[1].data[1]; + assignment.witness(component.W(13),start_row_index + row) = T[1].data[2]; + T = double_point(T); + + // Q is not used in doubling rows + assignment.witness(component.W(14),start_row_index + row) = 0; + assignment.witness(component.W(15),start_row_index + row) = 0; + assignment.witness(component.W(16),start_row_index + row) = 0; + assignment.witness(component.W(17),start_row_index + row) = 0; + assignment.witness(component.W(18),start_row_index + row) = 0; + assignment.witness(component.W(19),start_row_index + row) = 0; + + // lf + assignment.witness(component.W(20),start_row_index + row) = lf.data[0].data[0]; + assignment.witness(component.W(21),start_row_index + row) = lf.data[0].data[1]; + assignment.witness(component.W(22),start_row_index + row) = lf.data[0].data[2]; + assignment.witness(component.W(23),start_row_index + row) = lf.data[1].data[0]; + assignment.witness(component.W(24),start_row_index + row) = lf.data[1].data[1]; + assignment.witness(component.W(25),start_row_index + row) = lf.data[1].data[2]; + return g; + }; + + auto insert_row_addition = [ + &add_points, &assignment, &component, &start_row_index, &xP, &yP, &Q] + (fp6_element const& f, curve_point& T, std::size_t row) + { + fp6_element x, y, x1, y1, x2, y2, lf, g; + + x = fp6_element({ {xP, 0, 0}, {0, 0, 0} }); + y = fp6_element({ {yP, 0, 0}, {0, 0, 0} }); + + value_type nri = policy_type_fp3::extension_policy::non_residue.inversed(); + + // Untwist T and Q: E'/Fp3 -> E/Fp6 + x1 = fp6_element({ {T[0].data[1], T[0].data[2], T[0].data[0]*nri }, {0,0, 0} }); + y1 = fp6_element({ {0,0,0}, {T[1].data[2], T[1].data[0]*nri, T[1].data[1]*nri}}); + + x2 = fp6_element({ {Q[0].data[1], Q[0].data[2], Q[0].data[0]*nri }, {0,0, 0} }); + y2 = fp6_element({ {0,0,0}, {Q[1].data[2], Q[1].data[0]*nri, Q[1].data[1]*nri}}); + + lf = (y2-y1)*(x2-x1).inversed(); + g = f * (lf*(x-x1) + y1 - y); + + // f + assignment.witness(component.W(0),start_row_index + row) = f.data[0].data[0]; + assignment.witness(component.W(1),start_row_index + row) = f.data[0].data[1]; + assignment.witness(component.W(2),start_row_index + row) = f.data[0].data[2]; + assignment.witness(component.W(3),start_row_index + row) = f.data[1].data[0]; + assignment.witness(component.W(4),start_row_index + row) = f.data[1].data[1]; + assignment.witness(component.W(5),start_row_index + row) = f.data[1].data[2]; + + // P + assignment.witness(component.W(6),start_row_index + row ) = xP; + assignment.witness(component.W(7),start_row_index + row ) = yP; + + // T <- T+Q + assignment.witness(component.W(8), start_row_index + row) = T[0].data[0]; + assignment.witness(component.W(9), start_row_index + row) = T[0].data[1]; + assignment.witness(component.W(10),start_row_index + row) = T[0].data[2]; + assignment.witness(component.W(11),start_row_index + row) = T[1].data[0]; + assignment.witness(component.W(12),start_row_index + row) = T[1].data[1]; + assignment.witness(component.W(13),start_row_index + row) = T[1].data[2]; + T = add_points(T, Q); + + // Q + assignment.witness(component.W(14),start_row_index + row) = Q[0].data[0]; + assignment.witness(component.W(15),start_row_index + row) = Q[0].data[1]; + assignment.witness(component.W(16),start_row_index + row) = Q[0].data[2]; + assignment.witness(component.W(17),start_row_index + row) = Q[1].data[0]; + assignment.witness(component.W(18),start_row_index + row) = Q[1].data[1]; + assignment.witness(component.W(19),start_row_index + row) = Q[1].data[2]; + + // lf + assignment.witness(component.W(20),start_row_index + row) = lf.data[0].data[0]; + assignment.witness(component.W(21),start_row_index + row) = lf.data[0].data[1]; + assignment.witness(component.W(22),start_row_index + row) = lf.data[0].data[2]; + assignment.witness(component.W(23),start_row_index + row) = lf.data[1].data[0]; + assignment.witness(component.W(24),start_row_index + row) = lf.data[1].data[1]; + assignment.witness(component.W(25),start_row_index + row) = lf.data[1].data[2]; + return g; + }; + + std::size_t rel_row = 0; + + fp6_element f = fp6_element::one(); + curve_point T = Q; + + /* Miller loop */ + for(std::size_t i = 1; i < component.C_bin.size(); ++i) { + f = insert_row_doubling(f, T, rel_row++); + if (component.C_bin[i]) { + f = insert_row_addition(f, T, rel_row++); + } + } + + // The last row contains the result, f. + // f + assignment.witness(component.W(0),start_row_index + rel_row) = f.data[0].data[0]; + assignment.witness(component.W(1),start_row_index + rel_row) = f.data[0].data[1]; + assignment.witness(component.W(2),start_row_index + rel_row) = f.data[0].data[2]; + assignment.witness(component.W(3),start_row_index + rel_row) = f.data[1].data[0]; + assignment.witness(component.W(4),start_row_index + rel_row) = f.data[1].data[1]; + assignment.witness(component.W(5),start_row_index + rel_row) = f.data[1].data[2]; + + /* + // P + assignment.witness(component.W(6),start_row_index + rel_row ) = xP; + assignment.witness(component.W(7),start_row_index + rel_row ) = yP; + */ + + /* T is needed as previous row has constraints on it */ + assignment.witness(component.W(8), start_row_index + rel_row) = T[0].data[0]; + assignment.witness(component.W(9), start_row_index + rel_row) = T[0].data[1]; + assignment.witness(component.W(10),start_row_index + rel_row) = T[0].data[2]; + assignment.witness(component.W(11),start_row_index + rel_row) = T[1].data[0]; + assignment.witness(component.W(12),start_row_index + rel_row) = T[1].data[1]; + assignment.witness(component.W(13),start_row_index + rel_row) = T[1].data[2]; + + /* + // Q + assignment.witness(component.W(14),start_row_index + rel_row) = Q[0].data[0]; + assignment.witness(component.W(15),start_row_index + rel_row) = Q[0].data[1]; + assignment.witness(component.W(16),start_row_index + rel_row) = Q[0].data[2]; + assignment.witness(component.W(17),start_row_index + rel_row) = Q[1].data[0]; + assignment.witness(component.W(18),start_row_index + rel_row) = Q[1].data[1]; + assignment.witness(component.W(19),start_row_index + rel_row) = Q[1].data[2]; + */ + + return typename plonk_miller_loop::result_type( + component, start_row_index); + } + + template + std::vector + generate_gates( + plonk_miller_loop const& component, + circuit> &bp, + assignment> &assignment, + const typename plonk_miller_loop::input_type &instance_input) + { + using var = typename plonk_miller_loop::var; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using curve_type = nil::crypto3::algebra::curves::mnt6<298>; + using policy_type_fp3 = crypto3::algebra::fields::fp3; + + using fp3_constraint = detail::abstract_fp3_element< + constraint_type, BlueprintFieldType>; + + using fp6_constraint = detail::abstract_fp6_element< + constraint_type, BlueprintFieldType>; + + using value_type = typename BlueprintFieldType::value_type; + + fp3_constraint C3; + fp6_constraint C6; + + std::vector gate_list = {}; + constraint_type c_zero = constraint_type(), c_one = c_zero + 1; + constraint_type c_g1_a = c_zero + curve_type::g1_type<>::params_type::a; + constraint_type c_g2_a0 = c_zero + curve_type::g2_type<>::params_type::a.data[0]; + constraint_type c_g2_a1 = c_zero + curve_type::g2_type<>::params_type::a.data[1]; + constraint_type c_g2_a2 = c_zero + curve_type::g2_type<>::params_type::a.data[2]; + + value_type nri = policy_type_fp3::extension_policy::non_residue.inversed(); + + /* Constraints for the doubling gate + * 1. f = f_prev^2 * line_function_doubling(T,T,P) + * 2. T_next = T + T + */ + std::vector doubling_constrs = {}; + { + fp6_constraint + a6 = { c_g1_a, c_zero, c_zero, c_zero, }, + f = { + var(component.W(0), 0, true), + var(component.W(1), 0, true), + var(component.W(2), 0, true), + var(component.W(3), 0, true), + var(component.W(4), 0, true), + var(component.W(5), 0, true) + }, + fnext = { + var(component.W(0), 1, true), + var(component.W(1), 1, true), + var(component.W(2), 1, true), + var(component.W(3), 1, true), + var(component.W(4), 1, true), + var(component.W(5), 1, true) + }, + x = { + var(component.W(6), 0, true), + c_zero, c_zero, c_zero + }, + y = { + var(component.W(7), 0, true), + c_zero, c_zero, c_zero + }, + x1 = { + var(component.W(9), 0, true), + var(component.W(10), 0, true), + var(component.W(8), 0, true) * nri, + c_zero, c_zero, c_zero, + }, + y1 = { + c_zero, c_zero, c_zero, + var(component.W(13), 0, true), + var(component.W(11), 0, true) * nri, + var(component.W(12), 0, true) * nri, + }, + lf = { + var(component.W(20), 0, true), + var(component.W(21), 0, true), + var(component.W(22), 0, true), + var(component.W(23), 0, true), + var(component.W(24), 0, true), + var(component.W(25), 0, true) + }; + + C6 = lf*(2*y1) - (3*x1*x1 + a6); + for(auto const& c: C6.x) { + doubling_constrs.push_back(c); + } + + C6 = fnext - f*f*(lf*(x - x1) - (y - y1)); + for(auto const& c: C6.x) { + doubling_constrs.push_back(c); + } + } + + /* Constraints for point doubling: Tnext = T + T: + * Tnext.x = (3*T.x^2+a)^2/(2T.y)^2 - 2T.x + * Tnext.y = (3*T.x^2+a)/2T.y *(T.x-Tnext.x)-T.y + * Rewrite: + * (Tnext.x + 2*Tx) * (2*T.y)^2 - (3*T.x^2+a)^2 = 0 + * (Tnext.y + T.y) * (2*T.y) - (3*T.x^2+a)*(T.x-Tnext.x) = 0 + */ + fp3_constraint + a = {c_g2_a0, c_g2_a1, c_g2_a2}, + Tx = {var(component.W( 8), 0, true), var(component.W( 9), 0, true), var(component.W(10), 0, true)}, + Ty = {var(component.W(11), 0, true), var(component.W(12), 0, true), var(component.W(13), 0, true)}, + Tnx = {var(component.W( 8), 1, true), var(component.W( 9), 1, true), var(component.W(10), 1, true)}, + Tny = {var(component.W(11), 1, true), var(component.W(12), 1, true), var(component.W(13), 1, true)}; + + C3 = (Tnx + 2*Tx)*(2*Ty)*(2*Ty) - (3*Tx*Tx + a)*(3*Tx*Tx + a); + for(auto const& c: C3.data) { + doubling_constrs.push_back(c); + } + + C3 = (Tny + Ty)*(2*Ty) - (3*Tx*Tx + a)*(Tx - Tnx); + for(auto const& c: C3.data) { + doubling_constrs.push_back(c); + } + + gate_list.push_back(bp.add_gate(doubling_constrs)); + + /* Constraints for the addition row + * 1. f = f_prev * line_function_addition(T,Q,P) + * 2. T = T_prev + Q + */ + std::vector adding_constrs = {}; + { + fp6_constraint + f = { + var(component.W(0), 0, true), + var(component.W(1), 0, true), + var(component.W(2), 0, true), + var(component.W(3), 0, true), + var(component.W(4), 0, true), + var(component.W(5), 0, true) + }, + fnext = { + var(component.W(0), 1, true), + var(component.W(1), 1, true), + var(component.W(2), 1, true), + var(component.W(3), 1, true), + var(component.W(4), 1, true), + var(component.W(5), 1, true) + }, + x = { + var(component.W(6), 0, true), + c_zero, c_zero, c_zero + }, + y = { + var(component.W(7), 0, true), + c_zero, c_zero, c_zero + }, + x1 = { + var(component.W(9), 0, true), + var(component.W(10), 0, true), + var(component.W(8), 0, true) * nri, + c_zero, c_zero, c_zero, + }, + y1 = { + c_zero, c_zero, c_zero, + var(component.W(13), 0, true), + var(component.W(11), 0, true) * nri, + var(component.W(12), 0, true) * nri, + }, + x2 = { + var(component.W(15), 0, true), + var(component.W(16), 0, true), + var(component.W(14), 0, true) * nri, + c_zero, c_zero, c_zero, + }, + y2 = { + c_zero, c_zero, c_zero, + var(component.W(19), 0, true), + var(component.W(17), 0, true) * nri, + var(component.W(18), 0, true) * nri, + }, + lf = { + var(component.W(20), 0, true), + var(component.W(21), 0, true), + var(component.W(22), 0, true), + var(component.W(23), 0, true), + var(component.W(24), 0, true), + var(component.W(25), 0, true) + }; + + + C6 = lf*(x2 - x1) - (y2 - y1); + for(auto const& c: C6.x) { + adding_constrs.push_back(c); + } + + C6 = fnext - f*(lf*(x-x1) - (y-y1)); + for(auto const& c: C6.x) { + adding_constrs.push_back(c); + } + + } + + /* Constraints for point addition: Tnext = T + Q: + * Tnext.x = (Q.y - T.y)^2/(Q.x - T.x)^2- T.x - Q.x + * Tnext.y = (Q.y - T.y)/(Q.x - T.x)*(T.x - Tnext.x) - T.y + * Rewrite: + * (Tnext.x + T.x + Q.x)*(Q.x - T.x)^2 - (Q.y - T.y)^2 = 0 + * (Tnext.y + T.y)*(Q.x - T.x) - (Q.y - T.y) * (T.x - Tnext.x) = 0 + */ + fp3_constraint + Qx = {var(component.W(14), 0, true), var(component.W(15), 0, true), var(component.W(16), 0, true)}, + Qy = {var(component.W(17), 0, true), var(component.W(18), 0, true), var(component.W(19), 0, true)}; + + C3 = (Tnx + Tx + Qx)*(Qx - Tx)*(Qx - Tx) - (Qy - Ty)*(Qy - Ty); + for(auto const& c: C3.data) { + adding_constrs.push_back(c); + } + + C3 = (Tny + Ty)*(Qx - Tx) - (Qy - Ty)*(Tx - Tnx); + for(auto const& c: C3.data) { + adding_constrs.push_back(c); + } + + gate_list.push_back(bp.add_gate(adding_constrs)); + + return gate_list; + } + + template + void generate_copy_constraints( + plonk_miller_loop const& component, + circuit> &bp, + assignment> &assignment, + const typename plonk_miller_loop::input_type &instance_input, + const std::size_t start_row_index) + { + + using component_type = plonk_miller_loop; + using var = typename component_type::var; + + /* Copy constraints for f in the first row to constants (1,0,0,0,0,0) in constant column */ + bp.add_copy_constraint({ + var(0, start_row_index + 1, false, var::column_type::constant), + var(component.W(0), start_row_index, false)}); + bp.add_copy_constraint({ + var(0, start_row_index, false, var::column_type::constant), + var(component.W(1), start_row_index, false)}); + bp.add_copy_constraint({ + var(0, start_row_index, false, var::column_type::constant), + var(component.W(2), start_row_index, false)}); + bp.add_copy_constraint({ + var(0, start_row_index, false, var::column_type::constant), + var(component.W(3), start_row_index, false)}); + bp.add_copy_constraint({ + var(0, start_row_index, false, var::column_type::constant), + var(component.W(4), start_row_index, false)}); + bp.add_copy_constraint({ + var(0, start_row_index, false, var::column_type::constant), + var(component.W(5), start_row_index, false)}); + + /* T on the first row is Q */ + bp.add_copy_constraint({var(component.W( 8), start_row_index, false), instance_input.Q[0]}); + bp.add_copy_constraint({var(component.W( 9), start_row_index, false), instance_input.Q[1]}); + bp.add_copy_constraint({var(component.W(10), start_row_index, false), instance_input.Q[2]}); + bp.add_copy_constraint({var(component.W(11), start_row_index, false), instance_input.Q[3]}); + bp.add_copy_constraint({var(component.W(12), start_row_index, false), instance_input.Q[4]}); + bp.add_copy_constraint({var(component.W(13), start_row_index, false), instance_input.Q[5]}); + + std::size_t row = 0; + + /* Copy P and Q along the circuit */ + for(std::size_t i = 1; i < component.C_bin.size(); ++i) { + // P + bp.add_copy_constraint({var(component.W(6), start_row_index + row, false), instance_input.P[0]}); + bp.add_copy_constraint({var(component.W(7), start_row_index + row, false), instance_input.P[1]}); + ++row; + + if (component.C_bin[i]) { + // P + bp.add_copy_constraint({var(component.W(6), start_row_index + row, false), instance_input.P[0]}); + bp.add_copy_constraint({var(component.W(7), start_row_index + row, false), instance_input.P[1]}); + // Q + bp.add_copy_constraint({var(component.W(14), start_row_index + row, false), instance_input.Q[0]}); + bp.add_copy_constraint({var(component.W(15), start_row_index + row, false), instance_input.Q[1]}); + bp.add_copy_constraint({var(component.W(16), start_row_index + row, false), instance_input.Q[2]}); + bp.add_copy_constraint({var(component.W(17), start_row_index + row, false), instance_input.Q[3]}); + bp.add_copy_constraint({var(component.W(18), start_row_index + row, false), instance_input.Q[4]}); + bp.add_copy_constraint({var(component.W(19), start_row_index + row, false), instance_input.Q[5]}); + ++row; + } + } + } + + template + typename plonk_miller_loop::result_type + generate_circuit( + plonk_miller_loop const& component, + circuit> &bp, + assignment> &assignment, + const typename plonk_miller_loop::input_type &instance_input, + const std::size_t start_row_index) + { + using component_type = plonk_miller_loop; + using var = typename component_type::var; + + std::vector selector_index = generate_gates(component, bp, assignment, instance_input); + + generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); + generate_assignments_constant(component, assignment, instance_input, start_row_index); + + std::size_t row = 0; + for(std::size_t i = 1; i < component.C_bin.size(); i++) { + assignment.enable_selector(selector_index[0], start_row_index + row); + ++row; + if (component.C_bin[i]) { + assignment.enable_selector(selector_index[1], start_row_index + row); + ++row; + } + } + + return typename plonk_miller_loop::result_type( + component, start_row_index); + } + + template + void generate_assignments_constant( + const plonk_miller_loop &component, + assignment> &assignment, + const typename plonk_miller_loop::input_type &instance_input, + const std::size_t start_row_index) { + + // '0' to make copy-constraints with + assignment.constant(component.C(0), start_row_index) = 0; + // '1' to make copy-constraints with + assignment.constant(component.C(0), start_row_index + 1) = 1; + } + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MILLER_LOOP_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6129519ef..25de3f4d9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -190,7 +190,8 @@ set(HASHES_TESTS_FILES "hashes/r1cs/pedersen") set(PAIRING_TESTS_FILES - "algebra/pairing/weierstrass/plonk/mnt4_pairing" + "algebra/pairing/weierstrass/plonk/mnt4_miller_loop" + "algebra/pairing/weierstrass/plonk/mnt6_miller_loop" "algebra/pairing/weierstrass/plonk/mnt4_exponentiation" "algebra/pairing/weierstrass/plonk/mnt6_exponentiation" "algebra/pairing/weierstrass/r1cs/miller_loop" diff --git a/test/algebra/pairing/weierstrass/plonk/mnt4_pairing.cpp b/test/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.cpp similarity index 98% rename from test/algebra/pairing/weierstrass/plonk/mnt4_pairing.cpp rename to test/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.cpp index 9c3e6947d..4c2067746 100644 --- a/test/algebra/pairing/weierstrass/plonk/mnt4_pairing.cpp +++ b/test/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.cpp @@ -22,7 +22,7 @@ // SOFTWARE. //---------------------------------------------------------------------------// -#define BOOST_TEST_MODULE blueprint_plonk_pairing_mnt4_298 +#define BOOST_TEST_MODULE blueprint_plonk_mnt4_298_miller_loop #include @@ -109,7 +109,7 @@ static const std::size_t random_tests_amount = 5; BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) -BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt4_pairing_test) { +BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt4_miller_loop_test) { using curve_type = crypto3::algebra::curves::mnt4_298; using g2_group_type = typename curve_type::g2_type<>; using base_field_value = curve_type::base_field_type::value_type; diff --git a/test/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.cpp b/test/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.cpp new file mode 100644 index 000000000..eaabf0e2c --- /dev/null +++ b/test/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.cpp @@ -0,0 +1,158 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_mnt6_298_miller_loop + +#include + +#include +#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_mnt6_298_miller_loop(std::vector public_input, + std::vector expected_res) { + constexpr std::size_t PublicInputColumns = 1; + constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t SelectorColumns = (WitnessColumns == 12)? (4 + 8) : (4 + 9); + + zk::snark::plonk_table_description desc( + WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); + using ArithmetizationType = crypto3::zk::snark::plonk_constraint_system; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + constexpr std::size_t Lambda = 40; + using AssignmentType = nil::blueprint::assignment; + + using value_type = typename FieldType::value_type; + using var = crypto3::zk::snark::plonk_variable; + + using component_type = blueprint::components::mnt6_miller_loop; + + typename component_type::input_type instance_input = { + var(0,0, false, var::column_type::public_input), // xP + var(0,1, false, var::column_type::public_input), // yP + var(0,2, false, var::column_type::public_input), // xQ[0] + var(0,3, false, var::column_type::public_input), // xQ[1] + var(0,4, false, var::column_type::public_input), // xQ[2] + var(0,5, false, var::column_type::public_input), // yQ[0] + var(0,6, false, var::column_type::public_input), // yQ[1] + var(0,7, false, var::column_type::public_input) // yQ[2] + }; + + auto result_check = [&expected_res](AssignmentType const& assignment, + typename component_type::result_type const& real_res) { + #ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::cout << "mnt6-298 Miller loop: expected res VS output\n"; + for(std::size_t i = 0; i < 6; 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 < 6; i++) { + assert(expected_res[i] == var_value(assignment, real_res.output[i])); + } + }; + + std::array witnesses; + for (std::uint32_t i = 0; i < WitnessColumns; i++) { + witnesses[i] = i; + } + + component_type component_instance(witnesses, // witnesses + std::array{0}, // constants + std::array{} // public inputs + ); + + nil::crypto3::test_component ( + component_instance, desc, public_input, result_check, instance_input); +} + +static const std::size_t random_tests_amount = 5; + +BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) + +BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt6_miller_loop_test) { + using curve_type = crypto3::algebra::curves::mnt6_298; + using g2_group_type = typename curve_type::g2_type<>; + using base_field_value = curve_type::base_field_type::value_type; + using field_type = typename curve_type::g2_type<>::field_type::base_field_type; + + nil::crypto3::random::algebraic_engine generate_random; + boost::random::mt19937 seed_seq; + generate_random.seed(seed_seq); + + std::vector + AB = { + // A :[ + 0x013db0919ea4c2e5f62f79066ece6331ac4ca87e016bdbd7ac867d46a425b7fa24855c7a6ae3_cppui298, + 0x01a9409846dc9b92c558f3b99b90e1cc3027d9c0960b336be3939efd12c0fcc2321def266305_cppui298, + // ] + + // B :[ [ + 0x02962e2327b49141eb2d2435895ff40c49b3e071d5a18cddc504dd6ef5b62980bb8e8c0f5299_cppui298, + 0x002728d4acbbea319d32c137e09fe8fb40511ffbd4b6ae02a0d5c76b675b766a1c9bc91c0cf8_cppui298, + 0x025846902d06ccb8f14964d331ced68468f31441f366aaff4b0b5dd6eb720cd8f2460b789237_cppui298, + 0x0243124387533c863787fbaa1d58a942fe571660b77d80f3df036874309c7f1fcaef47611977_cppui298, + 0x00b5be310d4b9f76606e3206c435d1bee679ff0e1efe668e437e720d0e6e31965db04109f38c_cppui298, + 0x001638d3b614667d3bb2c0c2e6e2e8b675d5453cdf3dd15810e4b06fde235f90d7b48f4676c0_cppui298, + + // ]] + }, + AB_ML = { + 0x014034d3b8d52de5928a1b9073c373ce4107cbc98f9e34f6668de3898348133a193f2e34bfbc_cppui298, + 0x01261e232991430aa026545185afa3f20a4c86805f16e4c8ac27dffca62c23f1a7ca593f688e_cppui298, + 0x0069b3852c840dcc9563ecab53e1e649a5a4d4d268426b97bc8f9e77ffe3d555af7aebe41f69_cppui298, + 0x0230b995ed242adb3cea7e18971999dac6183622d15672f4b7e429dde4ce31be6d71619285d6_cppui298, + 0x02e690f97b2447b0c0f5a4349bc4d65cf87bc7f076df4ea689873e9231f0ad49520ea3e439c6_cppui298, + 0x016b1fef1c14c52e7f400545f8b548aed78670a0a47d7681b36ec686f504975ad26df1201fdc_cppui298 + }; + + std::cout << "mnt6-298 Miller loop test\n"; + test_mnt6_298_miller_loop(AB, AB_ML); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/test_plonk_component.hpp b/test/test_plonk_component.hpp index 73498e9fc..3db63a73f 100644 --- a/test/test_plonk_component.hpp +++ b/test/test_plonk_component.hpp @@ -252,7 +252,7 @@ namespace nil { variable.get() = assignment.get_batch_variable_map().at(variable); } } -#if 0 +#if 1 std::ofstream fass("circuit.tbl"); assignment.export_table(fass); fass.close(); From 0de7980984e39e5266717c8aa6e5a3cf025d0be6 Mon Sep 17 00:00:00 2001 From: Vasiliy Olekhov Date: Tue, 30 Apr 2024 00:09:06 +0300 Subject: [PATCH 6/7] Warnings fixed, removed zero witnesses, components proper stacking #355 --- .../plonk/non_native/mnt4_fp4_fixed_power.hpp | 3 +- .../plonk/non_native/mnt6_fp6_fixed_power.hpp | 1 - .../weierstrass/plonk/mnt4_exponentiation.hpp | 56 ++++---- .../weierstrass/plonk/mnt4_miller_loop.hpp | 122 ++++++++---------- .../weierstrass/plonk/mnt6_exponentiation.hpp | 40 +++--- .../weierstrass/plonk/mnt6_miller_loop.hpp | 60 +++------ .../weierstrass/plonk/mnt4_exponentiation.cpp | 27 +++- .../weierstrass/plonk/mnt4_miller_loop.cpp | 41 +++--- .../weierstrass/plonk/mnt6_exponentiation.cpp | 41 ++++-- .../weierstrass/plonk/mnt6_miller_loop.cpp | 40 +++--- test/test_plonk_component.hpp | 8 +- 11 files changed, 226 insertions(+), 213 deletions(-) diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp index 78e715171..e4a402c29 100644 --- a/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp @@ -99,7 +99,7 @@ namespace nil { public: using manifest_type = plonk_component_manifest; - const integral_type power/* = pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0*/; + const integral_type power; const std::vector exp_plan, exp_precompute; const std::size_t rows_amount; @@ -259,7 +259,6 @@ namespace nil { { using value_type = typename BlueprintFieldType::value_type; - using policy_type = typename component_type::policy_type; const std::vector exp_plan = component.exp_plan, diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp index 755449b06..87c141f8e 100644 --- a/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp @@ -259,7 +259,6 @@ namespace nil { { using value_type = typename BlueprintFieldType::value_type; - using policy_type = typename component_type::policy_type; const std::vector exp_plan = component.exp_plan, diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp index 76ed0c60f..eed3ce006 100644 --- a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp @@ -22,6 +22,10 @@ // SOFTWARE. //---------------------------------------------------------------------------// // @file Circuit for final exponentiation for MNT4 elliptic curve pairings +// Circuit summary: +// 4 witness, 0 constant, 7 gates, 198 rows +// 248 copy constraints +// each gate has 4 constraints, max degree is 2 //---------------------------------------------------------------------------// #ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_EXPONENTIATION_HPP @@ -69,8 +73,7 @@ namespace nil { // Gate 2: "Multiplication" // Gate 3: "Squaring" // Gate 4: "Cubing" - // Gate 5: "Fourth power" - // Gates 3-5 are used for powering to w0 + // Gates 3-4 are used for powering to w0 using namespace detail; template @@ -102,7 +105,8 @@ namespace nil { static gate_manifest get_gate_manifest( std::size_t witness_amount) { - static gate_manifest manifest = gate_manifest(gate_manifest_type()); + static gate_manifest manifest = gate_manifest(gate_manifest_type()) + .merge_with(fixed_power_type::get_gate_manifest(witness_amount, crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0)); return manifest; } @@ -118,12 +122,13 @@ namespace nil { constexpr static std::size_t get_rows_amount( std::size_t witness_amount) { - return fixed_power_type::get_rows_amount(witness_amount, + return fixed_power_type::get_rows_amount( + witness_amount, crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0) - + 9 - 1; + + 9; } - constexpr static const std::size_t gates_amount = 7; + constexpr static const std::size_t gates_amount = 3; const std::size_t rows_amount = get_rows_amount(0); struct input_type { @@ -191,9 +196,12 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using curve_type = nil::crypto3::algebra::curves::mnt4<298>; - using fixed_power_type = mnt4_fp4_fixed_power, BlueprintFieldType>; + using fixed_power_type = mnt4_fp4_fixed_power< + crypto3::zk::snark::plonk_constraint_system, BlueprintFieldType>; - fixed_power_type fixed_power_instance(component._W, component._C, component._PI, crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0); + fixed_power_type fixed_power_instance( + component._W, component._C, component._PI, + crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0); std::array x; @@ -263,7 +271,7 @@ namespace nil { } }); // Now elt3 holds x^(p^2-1)*w0 - // fill_row(elt3); + fill_row(elt3); // Final result is elt2*elt3 = x^((p^2-1)*p) * x^((p^2-1)*w0) = x^(p^2-1)*(p+w0) fill_row(elt2); @@ -324,10 +332,9 @@ namespace nil { R = Xn*X - Xp; constrs.clear(); - constrs.push_back(R[0]); - constrs.push_back(R[1]); - constrs.push_back(R[2]); - constrs.push_back(R[3]); + for(std::size_t i = 0; i < 4; ++i) { + constrs.push_back(R[i]); + } gate_list.push_back(bp.add_gate(constrs)); } @@ -339,10 +346,9 @@ namespace nil { R = Xn - X*Xp; constrs.clear(); - constrs.push_back(R[0]); - constrs.push_back(R[1]); - constrs.push_back(R[2]); - constrs.push_back(R[3]); + for(std::size_t i = 0; i < 4; ++i) { + constrs.push_back(R[i]); + } gate_list.push_back(bp.add_gate(constrs)); } @@ -380,7 +386,7 @@ namespace nil { // Copy from 5 row to R+6 row for(std::size_t i = 0; i < 4; ++i) { bp.add_copy_constraint({ - var(component.W(i), start_row_index + R + 6, false), + var(component.W(i), start_row_index + R + 7, false), var(component.W(i), start_row_index + 5, false), }); } @@ -402,7 +408,6 @@ namespace nil { fixed_power_type power_instance( component._W, component._C, component._PI, crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0); - std::size_t R = power_instance.rows_amount; std::vector selector_index = generate_gates(component, bp, assignment, instance_input); @@ -424,11 +429,18 @@ namespace nil { typename fixed_power_type::input_type power_input = { power_input_vars }; typename fixed_power_type::result_type power_output = generate_circuit(power_instance, bp, assignment, power_input, start_row_index + 6); + std::size_t R = power_instance.rows_amount; - // expect result at start_rows_index + 6 + R + // Copy from subcircuit result to R + 7 row + for(std::size_t i = 0; i < 4; ++i) { + bp.add_copy_constraint({ + var(component.W(i), start_row_index + R + 6, false), + power_output.output[i] + }); + } - // Multiplication gate - assignment.enable_selector(selector_index[2], start_row_index + R + 6); + // Multiplication gate at R + 7 + assignment.enable_selector(selector_index[2], start_row_index + R + 7); generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.hpp index 0d4627367..abe83c5cb 100644 --- a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.hpp +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.hpp @@ -22,6 +22,10 @@ // SOFTWARE. //---------------------------------------------------------------------------// // @file Declaration of Miller loop component for MNT4 pairings +// Circuit summary: +// 16 witness, 1 constant, 2 gates, 219 rows +// 724 copy constraints +// each gate has 12 constraints, max degree is 4 //---------------------------------------------------------------------------// #ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_MILLER_LOOP_HPP @@ -79,7 +83,7 @@ namespace nil { // // Each iteration of the Miller loop adds "doubling" row to the circuit: // - // f0 f1 f2 f3 P0 P1 T0 T1 T2 T3 Q0 Q1 Q2 Q3 L0 L1 L2 L3 + // f0 f1 f2 f3 P0 P1 T0 T1 T2 T3 Q0 Q1 Q2 Q3 [L0] [L1] L2 L3 // Gate 0: "doubling" // Constraints: // 0. UT = untwisted T @@ -88,7 +92,7 @@ namespace nil { // 3. T_next = T + T // // If current iteration needs to add addition, then "addition" row is inserted: - // f0 f1 f2 f3 P0 P1 T0 T1 T2 T3 Q0 Q1 Q2 Q3 L0 L1 L2 L3 + // f0 f1 f2 f3 P0 P1 T0 T1 T2 T3 Q0 Q1 Q2 Q3 [L0] [L1] L2 L3 // Gate 1: "addition" // Constraints: // 0. UT = untwisted T, UQ = untwisted Q @@ -101,18 +105,7 @@ namespace nil { // Q is copied only in addition rows. // Initial value f (1,0,0,0) is copied from constants column // - // Total number of copy constraints: 724 = 4+2*147+6*71 - // - // We can reduce number of copy constraints by next trick: - // 1. Copy Q in doubling rows too - // 2. To each gate (doubling and addition) add addition 6 constraints: - // w_i = w_i_rot(1), i = 2,4 (P), 9,10,11,12 (Q) - // 3. Leave copy constraints for P and Q on the first row - // Total number of copy constraints will be: - // 4+2+6 = 12 - // At the expense of adding 6 additional constraints to each gate - // - // Witnesses for L0 and L1 could be removed as they are always zero + // Witnesses for L0 and L1 are removed as they are always zero using namespace detail; using detail::base; @@ -153,7 +146,8 @@ namespace nil { static manifest_type get_manifest() { static manifest_type manifest = manifest_type( - std::shared_ptr(new manifest_single_value_param(18)), + std::shared_ptr( + new manifest_single_value_param(16)), true // constant column required ); return manifest; @@ -239,8 +233,6 @@ namespace nil { typename plonk_miller_loop::input_type const& instance_input, const std::uint32_t start_row_index) { - using component_type = plonk_miller_loop; - using var = typename component_type::var; using value_type = typename BlueprintFieldType::value_type; using policy_type_fp2 = crypto3::algebra::fields::fp2; @@ -254,15 +246,22 @@ namespace nil { yP = var_value(assignment, instance_input.P[1]); std::array - xQ = {var_value(assignment, instance_input.Q[0]), var_value(assignment, instance_input.Q[1])}, - yQ = {var_value(assignment, instance_input.Q[2]), var_value(assignment, instance_input.Q[3])}; + xQ = { + var_value(assignment, instance_input.Q[0]), + var_value(assignment, instance_input.Q[1]) + }, + yQ = { + var_value(assignment, instance_input.Q[2]), + var_value(assignment, instance_input.Q[3]) + }; curve_point Q = { fp2_element(xQ[0], xQ[1]), fp2_element(yQ[0], yQ[1])}; /* Calculate point doubling on E', affine coordinates */ auto double_point = [](curve_point const& T) { - fp2_element a(curve_type::g2_type<>::params_type::a), - lambda = (3*T[0].pow(2) + a) / (2*T[1]), + fp2_element + a(curve_type::g2_type<>::params_type::a), + lambda = (3*T[0].pow(2) + a) * (2*T[1]).inversed(), xR = lambda.pow(2) - 2*T[0], yR = (3*T[0])*lambda - lambda.pow(3) - T[1]; return curve_point({xR, yR}); @@ -271,7 +270,7 @@ namespace nil { /* Calculate point addition on E', affine coordinates */ auto add_points = [](curve_point const& T, curve_point const& Q) { fp2_element - lambda = (T[1] - Q[1])/(T[0] - Q[0]), + lambda = (T[1] - Q[1])*(T[0] - Q[0]).inversed(), xR = lambda*lambda - T[0] - Q[0], yR = (2*T[0] + Q[0])*lambda - lambda.pow(3) - T[1]; return curve_point({xR, yR}); @@ -338,10 +337,9 @@ namespace nil { assignment.witness(component.W(13),start_row_index + row) = 0; // lf - assignment.witness(component.W(14),start_row_index + row) = lf.data[0].data[0]; - assignment.witness(component.W(15),start_row_index + row) = lf.data[0].data[1]; - assignment.witness(component.W(16),start_row_index + row) = lf.data[1].data[0]; - assignment.witness(component.W(17),start_row_index + row) = lf.data[1].data[1]; + // lf.data[0] is always zero, not inserted into witness columns + assignment.witness(component.W(14),start_row_index + row) = lf.data[1].data[0]; + assignment.witness(component.W(15),start_row_index + row) = lf.data[1].data[1]; return g; }; @@ -390,10 +388,9 @@ namespace nil { assignment.witness(component.W(13),start_row_index + row) = Q[1].data[1]; // lf - assignment.witness(component.W(14),start_row_index + row) = lf.data[0].data[0]; - assignment.witness(component.W(15),start_row_index + row) = lf.data[0].data[1]; - assignment.witness(component.W(16),start_row_index + row) = lf.data[1].data[0]; - assignment.witness(component.W(17),start_row_index + row) = lf.data[1].data[1]; + // lf.data[0] is always zero, not inserted into witness columns + assignment.witness(component.W(14),start_row_index + row) = lf.data[1].data[0]; + assignment.witness(component.W(15),start_row_index + row) = lf.data[1].data[1]; return g; }; @@ -462,7 +459,7 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; - fp2_constraint C; + fp2_constraint C2; fp4_constraint C4; std::vector gate_list = {}; @@ -470,6 +467,7 @@ namespace nil { constraint_type c_g1_a = c_zero + curve_type::g1_type<>::params_type::a; constraint_type c_g2_a0 = c_zero + curve_type::g2_type<>::params_type::a.data[0]; constraint_type c_g2_a1 = c_zero + curve_type::g2_type<>::params_type::a.data[1]; + value_type nri = policy_type_fp2::extension_policy::non_residue.inversed(); /* Constraints for the doubling gate @@ -512,23 +510,20 @@ namespace nil { var(component.W(9), 0, true) * nri }, lf = { + c_zero, c_zero, var(component.W(14), 0, true), var(component.W(15), 0, true), - var(component.W(16), 0, true), - var(component.W(17), 0, true) }; C4 = lf*(2*y1) - (3*x1*x1 + a4); - doubling_constrs.push_back(C4[0]); - doubling_constrs.push_back(C4[1]); - doubling_constrs.push_back(C4[2]); - doubling_constrs.push_back(C4[3]); + for(auto const& c: C4.x) { + doubling_constrs.push_back(c); + } C4 = fnext - f*f*(lf*(x - x1) - (y - y1)); - doubling_constrs.push_back(C4[0]); - doubling_constrs.push_back(C4[1]); - doubling_constrs.push_back(C4[2]); - doubling_constrs.push_back(C4[3]); + for(auto const& c: C4.x) { + doubling_constrs.push_back(c); + } } /* Constraints for point doubling: Tnext = T + T: @@ -545,13 +540,13 @@ namespace nil { Tnx = {var(component.W(6), 1, true), var(component.W(7), 1, true)}, Tny = {var(component.W(8), 1, true), var(component.W(9), 1, true)}; - C = (Tnx + 2*Tx)*(2*Ty)*(2*Ty) - (3*Tx*Tx + a)*(3*Tx*Tx + a); - doubling_constrs.push_back(C[0]); - doubling_constrs.push_back(C[1]); + C2 = (Tnx + 2*Tx)*(2*Ty)*(2*Ty) - (3*Tx*Tx + a)*(3*Tx*Tx + a); + doubling_constrs.push_back(C2[0]); + doubling_constrs.push_back(C2[1]); - C = (Tny + Ty)*(2*Ty) - (3*Tx*Tx + a)*(Tx - Tnx); - doubling_constrs.push_back(C[0]); - doubling_constrs.push_back(C[1]); + C2 = (Tny + Ty)*(2*Ty) - (3*Tx*Tx + a)*(Tx - Tnx); + doubling_constrs.push_back(C2[0]); + doubling_constrs.push_back(C2[1]); gate_list.push_back(bp.add_gate(doubling_constrs)); @@ -603,23 +598,21 @@ namespace nil { var(component.W(13), 0, true) * nri }, lf = { + c_zero, c_zero, var(component.W(14), 0, true), var(component.W(15), 0, true), - var(component.W(16), 0, true), - var(component.W(17), 0, true) }; C4 = lf*(x2 - x1) - (y2 - y1); - adding_constrs.push_back(C4[0]); - adding_constrs.push_back(C4[1]); - adding_constrs.push_back(C4[2]); - adding_constrs.push_back(C4[3]); + for(auto const& c: C4.x) { + adding_constrs.push_back(c); + } C4 = fnext - f*(lf*(x-x1) - (y-y1)); - adding_constrs.push_back(C4[0]); - adding_constrs.push_back(C4[1]); - adding_constrs.push_back(C4[2]); - adding_constrs.push_back(C4[3]); + for(auto const& c: C4.x) { + adding_constrs.push_back(c); + } + } /* Constraints for point addition: Tnext = T + Q: @@ -633,13 +626,13 @@ namespace nil { Qx = {var(component.W(10), 0, true), var(component.W(11), 0, true)}, Qy = {var(component.W(12), 0, true), var(component.W(13), 0, true)}; - C = (Tnx + Tx + Qx)*(Qx - Tx)*(Qx - Tx) - (Qy - Ty)*(Qy - Ty); - adding_constrs.push_back(C[0]); - adding_constrs.push_back(C[1]); + C2 = (Tnx + Tx + Qx)*(Qx - Tx)*(Qx - Tx) - (Qy - Ty)*(Qy - Ty); + adding_constrs.push_back(C2[0]); + adding_constrs.push_back(C2[1]); - C = (Tny + Ty)*(Qx - Tx) - (Qy - Ty)*(Tx - Tnx); - adding_constrs.push_back(C[0]); - adding_constrs.push_back(C[1]); + C2 = (Tny + Ty)*(Qx - Tx) - (Qy - Ty)*(Tx - Tnx); + adding_constrs.push_back(C2[0]); + adding_constrs.push_back(C2[1]); gate_list.push_back(bp.add_gate(adding_constrs)); @@ -710,9 +703,6 @@ namespace nil { const typename plonk_miller_loop::input_type &instance_input, const std::size_t start_row_index) { - using component_type = plonk_miller_loop; - using var = typename component_type::var; - std::vector selector_index = generate_gates(component, bp, assignment, instance_input); generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.hpp index 90772c319..2b108ae8b 100644 --- a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.hpp +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.hpp @@ -22,6 +22,10 @@ // SOFTWARE. //---------------------------------------------------------------------------// // @file Circuit for final exponentiation for MNT6 elliptic curve pairings +// Circuit summary: +// 6 witness, 0 constant, 7 gates, 199 rows +// 366 copy constraints +// each gate has 6 constraints, max degree is 2 //---------------------------------------------------------------------------// #ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_EXPONENTIATION_HPP @@ -73,7 +77,6 @@ namespace nil { // Gate 3: "Multiplication" // Gate 4: "Squaring" // Gate 5: "Cubing" - // Gate 6: "Fourth power" // Gates 3-5 are used for powering to w_0 using namespace detail; @@ -123,12 +126,10 @@ namespace nil { constexpr static std::size_t get_rows_amount( std::size_t witness_amount) { - auto x = fixed_power_type::get_rows_amount( + return fixed_power_type::get_rows_amount( witness_amount, crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0) + 12; - std::cout << "Get rows: " << x << std::endl; - return x; } constexpr static const std::size_t gates_amount = 3; @@ -291,14 +292,13 @@ namespace nil { } }); // Now elt3 holds x^((p^3-1)*(p+1)*w0) - // Do not fill as sub-circuit output results are on the last row - // fill_row(elt3); + // The output of "fixed_power" circuit is copied into 9+R row - // 8+R-1: Final result is elt/elt3 = x^((p^3-1)*(p+1)*p) * x^(-(p^3-1)(p+1)*w0) = x^((p^3-1)*(p+1)*(p-w0)) + // 8+R: Final result is elt/elt3 = x^((p^3-1)*(p+1)*p) * x^(-(p^3-1)(p+1)*w0) = x^((p^3-1)*(p+1)*(p-w0)) fill_row(elt); fill_row(elt3); elt = elt*elt3.inversed(); - // 10+R-1 + // 10+R fill_row(elt); return typename plonk_mnt6_exponentiation::result_type( @@ -404,12 +404,12 @@ namespace nil { crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0); std::size_t R = power_instance.rows_amount; - // initial data in row 0 + // Initial data in row 0 for(std::size_t i = 0; i < 6; ++i) { bp.add_copy_constraint({var(component.W(i), start_row_index, false), instance_input.x[i]}); } - // initial data in row 4 + // Initial data in row 4 for(std::size_t i = 0; i < 6; ++i) { bp.add_copy_constraint({var(component.W(i), start_row_index + 4, false), instance_input.x[i]}); } @@ -421,15 +421,7 @@ namespace nil { var(component.W(i), start_row_index + 8, false), }); } - - // Copy from R+8 row to R+10 row - for(std::size_t i = 0; i < 6; ++i) { - bp.add_copy_constraint({ - var(component.W(i), start_row_index + R + 10, false), - var(component.W(i), start_row_index + R + 8, false), - }); - } - } + } template typename plonk_mnt6_exponentiation::result_type @@ -475,9 +467,15 @@ namespace nil { generate_circuit(power_instance, bp, assignment, power_input, start_row_index + 9); std::size_t R = power_instance.rows_amount; - // expect result at start_rows_index + R + 9 + // Copy from subcircuit result to R+10 row + for(std::size_t i = 0; i < 6; ++i) { + bp.add_copy_constraint({ + var(component.W(i), start_row_index + R + 10, false), + power_output.output[i] + }); + } - // Inverse division gate + // Division gate at R + 10 assignment.enable_selector(selector_index[1], start_row_index + R + 10); generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.hpp index c9b1c8132..a959f033a 100644 --- a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.hpp +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.hpp @@ -22,6 +22,10 @@ // SOFTWARE. //---------------------------------------------------------------------------// // @file Declaration of Miller loop component for MNT6 pairings +// Circuit summary: +// 23 witness, 1 constant, 2 gates, 219 rows +// 868 copy constraints +// each gate has 18 constraints, max degree 4 //---------------------------------------------------------------------------// #ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT6_MILLER_LOOP_HPP @@ -79,7 +83,7 @@ namespace nil { // // Each iteration of the Miller loop adds "doubling" row to the circuit: // - // f0 f1 f2 f3 f4 f5 P0 P1 T0 T1 T2 T3 T4 T5 Q0 Q1 Q2 Q3 Q4 Q5 L0 L1 L2 L3 L4 L5 + // f0 f1 f2 f3 f4 f5 P0 P1 T0 T1 T2 T3 T4 T5 Q0 Q1 Q2 Q3 Q4 Q5 [L0] [L1] [L2] L3 L4 L5 // Gate 0: "doubling" // Constraints: // 0. UT = untwisted T @@ -88,7 +92,7 @@ namespace nil { // 3. T_next = T + T // // If current iteration needs to add addition, then "addition" row is inserted: - // f0 f1 f2 f3 f4 f5 P0 P1 T0 T1 T2 T3 T4 T5 Q0 Q1 Q2 Q3 Q4 Q5 L0 L1 L2 L3 L4 L5 + // f0 f1 f2 f3 f4 f5 P0 P1 T0 T1 T2 T3 T4 T5 Q0 Q1 Q2 Q3 Q4 Q5 [L0] [L1] [L2] L3 L4 L5 // Gate 1: "addition" // Constraints: // 0. UT = untwisted T, UQ = untwisted Q @@ -101,18 +105,7 @@ namespace nil { // Q is copied only in addition rows. // Initial value f (1,0,0,0,0) is copied from constants column // - // Total number of copy constraints: 724 = 4+2*147+6*71 - // - // We can reduce number of copy constraints by next trick: - // 1. Copy Q in doubling rows too - // 2. To each gate (doubling and addition) add addition 6 constraints: - // w_i = w_i_rot(1), i = 6,7 (P), 14..19 (Q) - // 3. Leave copy constraints for P and Q on the first row - // Total number of copy constraints will be: - // 4+2+6 = 12 //xz - // At the expense of adding 6 additional constraints to each gate - // - // Witnesses for L0 and L1, L2 could be removed as they are always zero + // Witnesses for L0, L1, L2 are removed as they are always zero using namespace detail; using detail::base; @@ -153,7 +146,8 @@ namespace nil { static manifest_type get_manifest() { static manifest_type manifest = manifest_type( - std::shared_ptr(new manifest_single_value_param(18)), + std::shared_ptr( + new manifest_single_value_param(23)), true // constant column required ); return manifest; @@ -239,8 +233,6 @@ namespace nil { typename plonk_miller_loop::input_type const& instance_input, const std::uint32_t start_row_index) { - using component_type = plonk_miller_loop; - using var = typename component_type::var; using value_type = typename BlueprintFieldType::value_type; using policy_type_fp3 = crypto3::algebra::fields::fp3; @@ -269,7 +261,8 @@ namespace nil { /* Calculate point doubling on E', affine coordinates */ auto double_point = [](curve_point const& T) { - fp3_element a(curve_type::g2_type<>::params_type::a), + fp3_element + a(curve_type::g2_type<>::params_type::a), lambda = (3*T[0].pow(2) + a) * (2*T[1]).inversed(), xR = lambda.pow(2) - 2*T[0], yR = (3*T[0])*lambda - lambda.pow(3) - T[1]; @@ -353,12 +346,10 @@ namespace nil { assignment.witness(component.W(19),start_row_index + row) = 0; // lf - assignment.witness(component.W(20),start_row_index + row) = lf.data[0].data[0]; - assignment.witness(component.W(21),start_row_index + row) = lf.data[0].data[1]; - assignment.witness(component.W(22),start_row_index + row) = lf.data[0].data[2]; - assignment.witness(component.W(23),start_row_index + row) = lf.data[1].data[0]; - assignment.witness(component.W(24),start_row_index + row) = lf.data[1].data[1]; - assignment.witness(component.W(25),start_row_index + row) = lf.data[1].data[2]; + // lf.data[0] is always zero, not inserted into witness columns + assignment.witness(component.W(20),start_row_index + row) = lf.data[1].data[0]; + assignment.witness(component.W(21),start_row_index + row) = lf.data[1].data[1]; + assignment.witness(component.W(22),start_row_index + row) = lf.data[1].data[2]; return g; }; @@ -413,12 +404,10 @@ namespace nil { assignment.witness(component.W(19),start_row_index + row) = Q[1].data[2]; // lf - assignment.witness(component.W(20),start_row_index + row) = lf.data[0].data[0]; - assignment.witness(component.W(21),start_row_index + row) = lf.data[0].data[1]; - assignment.witness(component.W(22),start_row_index + row) = lf.data[0].data[2]; - assignment.witness(component.W(23),start_row_index + row) = lf.data[1].data[0]; - assignment.witness(component.W(24),start_row_index + row) = lf.data[1].data[1]; - assignment.witness(component.W(25),start_row_index + row) = lf.data[1].data[2]; + // lf.data[0] is always zero, not inserted into witness columns + assignment.witness(component.W(20),start_row_index + row) = lf.data[1].data[0]; + assignment.witness(component.W(21),start_row_index + row) = lf.data[1].data[1]; + assignment.witness(component.W(22),start_row_index + row) = lf.data[1].data[2]; return g; }; @@ -550,12 +539,10 @@ namespace nil { var(component.W(12), 0, true) * nri, }, lf = { + c_zero, c_zero, c_zero, var(component.W(20), 0, true), var(component.W(21), 0, true), var(component.W(22), 0, true), - var(component.W(23), 0, true), - var(component.W(24), 0, true), - var(component.W(25), 0, true) }; C6 = lf*(2*y1) - (3*x1*x1 + a6); @@ -651,12 +638,10 @@ namespace nil { var(component.W(18), 0, true) * nri, }, lf = { + c_zero, c_zero, c_zero, var(component.W(20), 0, true), var(component.W(21), 0, true), var(component.W(22), 0, true), - var(component.W(23), 0, true), - var(component.W(24), 0, true), - var(component.W(25), 0, true) }; @@ -772,9 +757,6 @@ namespace nil { const typename plonk_miller_loop::input_type &instance_input, const std::size_t start_row_index) { - using component_type = plonk_miller_loop; - using var = typename component_type::var; - std::vector selector_index = generate_gates(component, bp, assignment, instance_input); generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); diff --git a/test/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.cpp b/test/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.cpp index 9ee6868c7..d81a71d0e 100644 --- a/test/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.cpp +++ b/test/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.cpp @@ -54,7 +54,7 @@ template void test_mnt4_298_exponentiation(std::vector public_input, std::vector expected_res) { constexpr std::size_t PublicInputColumns = 1; - constexpr std::size_t ConstantColumns = 1; + constexpr std::size_t ConstantColumns = 0; constexpr std::size_t SelectorColumns = 8; zk::snark::plonk_table_description desc( @@ -110,7 +110,6 @@ BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt4_pairing_test) { using curve_type = crypto3::algebra::curves::mnt4_298; using gt_group_type = typename curve_type::gt_type; - using base_field_value = curve_type::base_field_type::value_type; using field_type = typename curve_type::gt_type::base_field_type; nil::crypto3::random::algebraic_engine generate_random; @@ -133,6 +132,30 @@ BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt4_pairing_test) { std::cout << "MNT4-298 Final exponentiation test\n"; test_mnt4_298_exponentiation(AB_ML, AB_FE); + + for(std::size_t i = 0; i < random_tests_amount; ++i) { + typename gt_group_type::value_type + A = crypto3::algebra::random_element(), + A_FE = final_exponentiation(A); + + std::vector + input = { + A.data[0].data[0], + A.data[0].data[1], + A.data[1].data[0], + A.data[1].data[1], + }, + result = { + A_FE.data[0].data[0], + A_FE.data[0].data[1], + A_FE.data[1].data[0], + A_FE.data[1].data[1], + }; + + std::cout << "mnt4-298 Final exponentiation random test " << i << std::endl; + test_mnt4_298_exponentiation(input, result); + + } } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.cpp b/test/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.cpp index 4c2067746..d1a30e29b 100644 --- a/test/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.cpp +++ b/test/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.cpp @@ -36,7 +36,6 @@ #include #include -#include #include @@ -55,7 +54,7 @@ void test_mnt4_298_miller_loop(std::vector publi std::vector expected_res) { constexpr std::size_t PublicInputColumns = 1; constexpr std::size_t ConstantColumns = 1; - constexpr std::size_t SelectorColumns = (WitnessColumns == 12)? (4 + 8) : (4 + 9); + constexpr std::size_t SelectorColumns = 2; zk::snark::plonk_table_description desc( WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); @@ -70,13 +69,17 @@ void test_mnt4_298_miller_loop(std::vector publi using component_type = blueprint::components::mnt4_miller_loop; typename component_type::input_type instance_input = { - var(0,0, false, var::column_type::public_input), // xP - var(0,1, false, var::column_type::public_input), // yP - var(0,2, false, var::column_type::public_input), // xQ[0] - var(0,3, false, var::column_type::public_input), // xQ[1] - var(0,4, false, var::column_type::public_input), // yQ[0] - var(0,5, false, var::column_type::public_input) // yQ[1] - }; + { + var(0,0, false, var::column_type::public_input), // xP + var(0,1, false, var::column_type::public_input), // yP + }, + { + var(0,2, false, var::column_type::public_input), // xQ[0] + var(0,3, false, var::column_type::public_input), // xQ[1] + var(0,4, false, var::column_type::public_input), // yQ[0] + var(0,5, false, var::column_type::public_input) // yQ[1] + } + }; auto result_check = [&expected_res](AssignmentType const& assignment, typename component_type::result_type const& real_res) { @@ -105,20 +108,12 @@ void test_mnt4_298_miller_loop(std::vector publi component_instance, desc, public_input, result_check, instance_input); } -static const std::size_t random_tests_amount = 5; - BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt4_miller_loop_test) { using curve_type = crypto3::algebra::curves::mnt4_298; - using g2_group_type = typename curve_type::g2_type<>; - using base_field_value = curve_type::base_field_type::value_type; using field_type = typename curve_type::g2_type<>::field_type::base_field_type; - nil::crypto3::random::algebraic_engine generate_random; - boost::random::mt19937 seed_seq; - generate_random.seed(seed_seq); - std::vector AB = { // A :[ @@ -127,11 +122,11 @@ BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt4_miller_loop_test) { // ] // B :[ [ - 0x012212f4ac5a2b6262dcd15a0fb4e54d276d734d80e3868dc93a074b3a9ebeb598641aa2310d_cppui298, - 0x017600e8757679e06b66de2c48b3370e582443d4c0091ef1e6d96dadb92150ff642709dd806b_cppui298, - 0x02a1135b45f576b0988c2f5e852def5e829508beddae07427cc68929ffbeaa49de4d370cfa69_cppui298, - 0x0246c479956c92096a1dfa7cdb992b53ecb05f96d581fcb755045898fb459fd569753da2c2a7_cppui298 - // ]] + 0x012212f4ac5a2b6262dcd15a0fb4e54d276d734d80e3868dc93a074b3a9ebeb598641aa2310d_cppui298, + 0x017600e8757679e06b66de2c48b3370e582443d4c0091ef1e6d96dadb92150ff642709dd806b_cppui298, + 0x02a1135b45f576b0988c2f5e852def5e829508beddae07427cc68929ffbeaa49de4d370cfa69_cppui298, + 0x0246c479956c92096a1dfa7cdb992b53ecb05f96d581fcb755045898fb459fd569753da2c2a7_cppui298 + // ]] }, AB_ML = { 0x01f3f02a39499cca91c7c3a108cc0721047455bc2def95bcb613a1749c1bbe0fb0d88088699b_cppui298, @@ -141,7 +136,7 @@ BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt4_miller_loop_test) { }; std::cout << "MNT4-298 Miller loop test\n"; - test_mnt4_298_miller_loop(AB, AB_ML); + test_mnt4_298_miller_loop(AB, AB_ML); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.cpp b/test/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.cpp index 5abeeb2f6..581a92cb8 100644 --- a/test/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.cpp +++ b/test/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.cpp @@ -112,7 +112,6 @@ BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt6_pairing_test) { using curve_type = crypto3::algebra::curves::mnt6_298; using gt_group_type = typename curve_type::gt_type; - using base_field_value = curve_type::base_field_type::value_type; using field_type = typename curve_type::gt_type::base_field_type; nil::crypto3::random::algebraic_engine generate_random; @@ -129,14 +128,6 @@ BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt6_pairing_test) { 0x01b5c328e47a03bddb00f0992ddbd9945af3e0f4bb8244d29b9b24ca2390e92ecc5666a76191_cppui298 }, AB_FE = { - /* - 0x15dec519566f20aa6a3bd4ddb0cee93f9ea157499f20273124883934daeea1867ea53ac4b9b_cppui298, - 0x3b5fc396c41a54c7c661b3d34ead6f284a50886534f036c004eb31d8d7cb061d3419f6a9d58_cppui298, - 0x1b79ac259f41bfdc8aa623c65075d0a0fb1823467b174c4ac82cada904d96f3640fd491baf7_cppui298, - 0x667fef1c7f96557416d9728951128edb921b4ecad5592fd898db5867bf40dfa20de956a717_cppui298, - 0x39143f4b1cee4646b2a1a77d27fc21aead95a0922a4a5f9facb82d88120b01737fafb1f1d8d_cppui298, - 0x2f84e7545b29729839b5871c25a131756558cd8bba38d2ef7439f20b5cb54be2a271a3c1bba_cppui298, - */ 0x025aba200efbefa81017a858457abfc1e83ce8f7e92788e414fe90bacd465395dec8463bf09a_cppui298, 0x002ca86c9adeb0cfd62db143cc3fc5b6adb9fd09419a43b6c5b841f129a0ef71fa3881a4b743_cppui298, 0x00e7bc013d484cc770ddcaa994422f153265c143a64549b916f6893c2e2ab5458e7c3ea5f3d7_cppui298, @@ -145,8 +136,36 @@ BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt6_pairing_test) { 0x037f4a31f3bc24e9876d57a5b224d8f6475d36407e092bc03144d1bf042a0aee471639db2439_cppui298 }; - std::cout << "mnt6-298 Final exponentiation test\n"; - test_mnt6_298_exponentiation(AB_ML, AB_FE); + std::cout << "mnt6-298 Final exponentiation test\n"; + test_mnt6_298_exponentiation(AB_ML, AB_FE); + + for(std::size_t i = 0; i < random_tests_amount; ++i) { + typename gt_group_type::value_type + A = crypto3::algebra::random_element(), + A_FE = final_exponentiation(A); + + std::vector + input = { + A.data[0].data[0], + A.data[0].data[1], + A.data[0].data[2], + A.data[1].data[0], + A.data[1].data[1], + A.data[1].data[2], + }, + result = { + A_FE.data[0].data[0], + A_FE.data[0].data[1], + A_FE.data[0].data[2], + A_FE.data[1].data[0], + A_FE.data[1].data[1], + A_FE.data[1].data[2], + }; + + std::cout << "mnt6-298 Final exponentiation random test " << i << std::endl; + test_mnt6_298_exponentiation(input, result); + + } } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.cpp b/test/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.cpp index eaabf0e2c..c81abdb0b 100644 --- a/test/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.cpp +++ b/test/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.cpp @@ -40,7 +40,6 @@ #include #include -#include #include @@ -59,7 +58,7 @@ void test_mnt6_298_miller_loop(std::vector publi std::vector expected_res) { constexpr std::size_t PublicInputColumns = 1; constexpr std::size_t ConstantColumns = 1; - constexpr std::size_t SelectorColumns = (WitnessColumns == 12)? (4 + 8) : (4 + 9); + constexpr std::size_t SelectorColumns = 2; zk::snark::plonk_table_description desc( WitnessColumns, PublicInputColumns, ConstantColumns, SelectorColumns); @@ -74,15 +73,19 @@ void test_mnt6_298_miller_loop(std::vector publi using component_type = blueprint::components::mnt6_miller_loop; typename component_type::input_type instance_input = { - var(0,0, false, var::column_type::public_input), // xP - var(0,1, false, var::column_type::public_input), // yP - var(0,2, false, var::column_type::public_input), // xQ[0] - var(0,3, false, var::column_type::public_input), // xQ[1] - var(0,4, false, var::column_type::public_input), // xQ[2] - var(0,5, false, var::column_type::public_input), // yQ[0] - var(0,6, false, var::column_type::public_input), // yQ[1] - var(0,7, false, var::column_type::public_input) // yQ[2] - }; + { + var(0,0, false, var::column_type::public_input), // xP + var(0,1, false, var::column_type::public_input), // yP + }, + { + var(0,2, false, var::column_type::public_input), // xQ[0] + var(0,3, false, var::column_type::public_input), // xQ[1] + var(0,4, false, var::column_type::public_input), // xQ[2] + var(0,5, false, var::column_type::public_input), // yQ[0] + var(0,6, false, var::column_type::public_input), // yQ[1] + var(0,7, false, var::column_type::public_input) // yQ[2] + } + }; auto result_check = [&expected_res](AssignmentType const& assignment, typename component_type::result_type const& real_res) { @@ -111,19 +114,11 @@ void test_mnt6_298_miller_loop(std::vector publi component_instance, desc, public_input, result_check, instance_input); } -static const std::size_t random_tests_amount = 5; - BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt6_miller_loop_test) { using curve_type = crypto3::algebra::curves::mnt6_298; - using g2_group_type = typename curve_type::g2_type<>; - using base_field_value = curve_type::base_field_type::value_type; - using field_type = typename curve_type::g2_type<>::field_type::base_field_type; - - nil::crypto3::random::algebraic_engine generate_random; - boost::random::mt19937 seed_seq; - generate_random.seed(seed_seq); + using field_type = typename curve_type::base_field_type; std::vector AB = { @@ -139,7 +134,6 @@ BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt6_miller_loop_test) { 0x0243124387533c863787fbaa1d58a942fe571660b77d80f3df036874309c7f1fcaef47611977_cppui298, 0x00b5be310d4b9f76606e3206c435d1bee679ff0e1efe668e437e720d0e6e31965db04109f38c_cppui298, 0x001638d3b614667d3bb2c0c2e6e2e8b675d5453cdf3dd15810e4b06fde235f90d7b48f4676c0_cppui298, - // ]] }, AB_ML = { @@ -151,8 +145,8 @@ BOOST_AUTO_TEST_CASE(blueprint_plonk_mnt6_miller_loop_test) { 0x016b1fef1c14c52e7f400545f8b548aed78670a0a47d7681b36ec686f504975ad26df1201fdc_cppui298 }; - std::cout << "mnt6-298 Miller loop test\n"; - test_mnt6_298_miller_loop(AB, AB_ML); + std::cout << "mnt6-298 Miller loop test\n"; + test_mnt6_298_miller_loop(AB, AB_ML); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/test_plonk_component.hpp b/test/test_plonk_component.hpp index 3db63a73f..268e5825d 100644 --- a/test/test_plonk_component.hpp +++ b/test/test_plonk_component.hpp @@ -252,7 +252,8 @@ namespace nil { variable.get() = assignment.get_batch_variable_map().at(variable); } } -#if 1 + +#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED std::ofstream fass("circuit.tbl"); assignment.export_table(fass); fass.close(); @@ -261,6 +262,7 @@ namespace nil { bp.export_circuit(fcirc); fcirc.close(); #endif + result_check(assignment, component_result); if constexpr (!PrivateInput) { @@ -283,8 +285,8 @@ namespace nil { // blueprint::detail::export_connectedness_zones( // zones, assignment, instance_input.all_vars(), start_row, rows_after_batching - start_row, std::cout); - // BOOST_ASSERT_MSG(is_connected, - // "Component disconnected! See comment above this assert for a way to output a visual representation of the connectedness graph."); + BOOST_ASSERT_MSG(is_connected, + "Component disconnected! See comment above this assert for a way to output a visual representation of the connectedness graph."); } desc.usable_rows_amount = assignment.rows_amount(); From 73036c2d022ec582e1a1462fba2e7aeaa4a089a1 Mon Sep 17 00:00:00 2001 From: Vasiliy Olekhov Date: Tue, 30 Apr 2024 00:20:21 +0300 Subject: [PATCH 7/7] Comments clarification #355 --- .../pairing/weierstrass/plonk/mnt4_exponentiation.hpp | 4 +--- .../pairing/weierstrass/plonk/mnt6_exponentiation.hpp | 9 +++------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp index eed3ce006..a9b42ca43 100644 --- a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp @@ -71,9 +71,7 @@ namespace nil { // Gate 0: Raising to power p, "Frobenius map" // Gate 1: "Division in Fp4" // Gate 2: "Multiplication" - // Gate 3: "Squaring" - // Gate 4: "Cubing" - // Gates 3-4 are used for powering to w0 + // Plus 4 gates from circuit of "fixed_power" using namespace detail; template diff --git a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.hpp b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.hpp index 2b108ae8b..eef17c2cc 100644 --- a/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.hpp +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.hpp @@ -72,12 +72,9 @@ namespace nil { // Circuit requires 6 witnesses, 6 inputs and 6 outputs // 6 gates are used: // Gate 0: Raising to power p, "Frobenius map" - // Gate 1: "Division in Fp6" : x_next = x_prev / x - // Gate 2: "Inverse Division in Fp6": x_next = x / x_prev - // Gate 3: "Multiplication" - // Gate 4: "Squaring" - // Gate 5: "Cubing" - // Gates 3-5 are used for powering to w_0 + // Gate 1: "Division in Fp6" + // Gate 2: "Multiplication" + // Plus 4 gates from circuit of "fixed_power" using namespace detail; template