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..e7086e420 --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp3.hpp @@ -0,0 +1,79 @@ +//---------------------------------------------------------------------------// +// 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 +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + template + class abstract_fp3_element { + public: + using policy_type_fp3 = crypto3::algebra::fields::fp3; + 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 = 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], + data[0]*other[2] + data[1]*other[1] + data[2]*other[0] + }; + } + + constexpr abstract_fp3_element operator*(const int x) { + return { data[0]*x, data[1]*x, data[2]*x }; + } + friend abstract_fp3_element operator*(const int x, abstract_fp3_element const& e) { + return { e[0]*x, e[1]*x, e[2]*x }; + } + constexpr abstract_fp3_element operator+(abstract_fp3_element const& other) { + return { data[0] + other[0], data[1] + other[1], data[2] + other[2] }; + } + constexpr abstract_fp3_element operator-(abstract_fp3_element const& other) { + return { data[0] - other[0], data[1] - other[1], data[2] - other[2] }; + } + }; + + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP3_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp4.hpp b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp4.hpp new file mode 100644 index 000000000..b8602e65d --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/abstract_fp4.hpp @@ -0,0 +1,87 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Vasiliy Olekhov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of F_p^4 elements over an abstract entity (to be used with constraints) +// with F_p^4 = Fp^2 over Fp^2: +// Fp^4 = Fp^2[x]/(x^2 - u), u = (0,1) +// Fp^2 = Fp[y]/(y^2 - v), v = 17 (0x11), u^2 = v +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP4_HPP +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_ABSTRACT_FP4_HPP + +#include +#include + +#include + +namespace nil { + namespace blueprint { + namespace components { + namespace detail { + + template + class abstract_fp4_element{ + public: + using policy_type_fp4 = crypto3::algebra::fields::fp4; + std::array x; + + T& operator[](std::size_t idx) { + return x[idx]; + } + const T& operator[](std::size_t idx) const { + return x[idx]; + } + + constexpr abstract_fp4_element operator*(abstract_fp4_element const& y) { + // Devegili et al - Multiplication and squaring in pairing-friendly fields + // https://eprint.iacr.org/2006/471.pdf + constexpr auto s = policy_type_fp4::extension_policy::non_residue; + auto d00 = x[0b00]*y[0b00] + s*(x[0b01]*y[0b01] + x[0b10]*y[0b11] + x[0b11]*y[0b10]); + auto d01 = x[0b00]*y[0b01] + x[0b10]*y[0b10] + x[0b01]*y[0b00] + s*x[0b11]*y[0b11]; + auto d10 = x[0b00]*y[0b10] + x[0b10]*y[0b00] + s*(x[0b01]*y[0b11] + x[0b11]*y[0b01]); + auto d11 = x[0b00]*y[0b11] + x[0b01]*y[0b10] + x[0b10]*y[0b01] + x[0b11]*y[0b00]; + + return { d00, d01, d10, d11}; + } + + constexpr abstract_fp4_element operator*(const int a) { + return { x[0]*a, x[1]*a, x[2]*a, x[3]*a }; + } + friend abstract_fp4_element operator*(const int a, abstract_fp4_element const& x) { + return { x[0]*a, x[1]*a, x[2]*a, x[3]*a }; + } + constexpr abstract_fp4_element operator+(abstract_fp4_element const& y) { + return { x[0] + y[0], x[1] + y[1], x[2] + y[2], x[3] + y[3] }; + } + constexpr abstract_fp4_element operator-(abstract_fp4_element const& y) { + return { x[0] - y[0], x[1] - y[1], x[2] - y[2], x[3] - y[3] }; + } + }; + + } // namespace detail + } // namespace components + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_abstract_FP4_HPP diff --git a/include/nil/blueprint/components/algebra/fields/plonk/non_native/detail/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 new file mode 100644 index 000000000..e4a402c29 --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt4_fp4_fixed_power.hpp @@ -0,0 +1,500 @@ +//---------------------------------------------------------------------------// +// 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 fp4_element = typename policy_type::value_type; + using fp4_constraint = detail::abstract_fp4_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; + + const std::vector exp_plan, exp_precompute; + 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), + (std::count(exp_plan.begin(),exp_plan.end(),3) > 0), + (std::count(exp_plan.begin(),exp_plan.end(),2) > 0) }; + } + public: + 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 { + 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, + 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, + 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, + 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(), power)) + { }; + + template + mnt4_fp4_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(), power)) + { }; + + mnt4_fp4_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(), power)) + { }; + }; + + /* */ + + template + using component_type = mnt4_fp4_fixed_power< + crypto3::zk::snark::plonk_constraint_system, + 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; + + 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) { + 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++; + }; + + 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..87c141f8e --- /dev/null +++ b/include/nil/blueprint/components/algebra/fields/plonk/non_native/mnt6_fp6_fixed_power.hpp @@ -0,0 +1,500 @@ +//---------------------------------------------------------------------------// +// 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, 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::fp6_2over3; + using integral_type = typename policy_type::extension_policy::integral_type; + using fp6_element = typename policy_type::value_type; + using fp6_constraint = detail::abstract_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 { + 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(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, + 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, + 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(), 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(), 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(), power)) + { }; + }; + + /* */ + + template + using component_type = mnt6_fp6_fixed_power< + crypto3::zk::snark::plonk_constraint_system, + 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; + + 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 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 < 6; 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 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::fp6_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_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..a9b42ca43 --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.hpp @@ -0,0 +1,452 @@ +//---------------------------------------------------------------------------// +// 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 +// 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 +#define CRYPTO3_BLUEPRINT_COMPONENTS_PLONK_MNT4_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 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" + // Plus 4 gates from circuit of "fixed_power" + 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< + 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 mnt4_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) + { + return fixed_power_type::get_rows_amount( + witness_amount, + crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0) + + 9; + } + + 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]}; + } + }; + + 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< + 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 < 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(); + for(std::size_t i = 0; i < 4; ++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 < 4; ++i) { + constrs.push_back(R[i]); + } + + 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 + 7, 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::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); + std::size_t R = power_instance.rows_amount; + + // 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 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); + + 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..abe83c5cb --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.hpp @@ -0,0 +1,741 @@ +//---------------------------------------------------------------------------// +// 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 +// 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 +#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 + // + // Witnesses for L0 and L1 are 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(16)), + 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 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]).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) { + fp2_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_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 + // 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; + }; + + 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 + // 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; + }; + + 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 C2; + 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 = { + c_zero, c_zero, + var(component.W(14), 0, true), + var(component.W(15), 0, true), + }; + + C4 = lf*(2*y1) - (3*x1*x1 + a4); + for(auto const& c: C4.x) { + doubling_constrs.push_back(c); + } + + C4 = fnext - f*f*(lf*(x - x1) - (y - y1)); + for(auto const& c: C4.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 + */ + 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)}; + + 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]); + + 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)); + + /* 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 = { + c_zero, c_zero, + var(component.W(14), 0, true), + var(component.W(15), 0, true), + }; + + C4 = lf*(x2 - x1) - (y2 - y1); + for(auto const& c: C4.x) { + adding_constrs.push_back(c); + } + + C4 = fnext - f*(lf*(x-x1) - (y-y1)); + for(auto const& c: C4.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 + */ + 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)}; + + C2 = (Tnx + Tx + Qx)*(Qx - Tx)*(Qx - Tx) - (Qy - Ty)*(Qy - Ty); + adding_constrs.push_back(C2[0]); + adding_constrs.push_back(C2[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)); + + 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) + { + 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/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..eef17c2cc --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.hpp @@ -0,0 +1,487 @@ +//---------------------------------------------------------------------------// +// 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 +// 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 +#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" + // Gate 2: "Multiplication" + // Plus 4 gates from circuit of "fixed_power" + 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) + { + return fixed_power_type::get_rows_amount( + witness_amount, + crypto3::algebra::pairing::detail::pairing_params::final_exponent_last_chunk_abs_of_w0) + + 12; + } + + 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) + // The output of "fixed_power" circuit is copied into 9+R row + + // 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 + 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), + }); + } + } + + 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; + + // 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] + }); + } + + // 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); + + 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/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..a959f033a --- /dev/null +++ b/include/nil/blueprint/components/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.hpp @@ -0,0 +1,795 @@ +//---------------------------------------------------------------------------// +// 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 +// 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 +#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 + // + // Witnesses for L0, L1, L2 are 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(23)), + 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 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 + // 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; + }; + + 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 + // 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; + }; + + 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 = { + c_zero, c_zero, c_zero, + var(component.W(20), 0, true), + var(component.W(21), 0, true), + var(component.W(22), 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 = { + c_zero, c_zero, c_zero, + var(component.W(20), 0, true), + var(component.W(21), 0, true), + var(component.W(22), 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) + { + 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..25de3f4d9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -80,6 +80,8 @@ 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" + "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" @@ -188,6 +190,10 @@ set(HASHES_TESTS_FILES "hashes/r1cs/pedersen") set(PAIRING_TESTS_FILES + "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" "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..4275238cb --- /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::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/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/mnt4_exponentiation.cpp b/test/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.cpp new file mode 100644 index 000000000..d81a71d0e --- /dev/null +++ b/test/algebra/pairing/weierstrass/plonk/mnt4_exponentiation.cpp @@ -0,0 +1,161 @@ +//---------------------------------------------------------------------------// +// 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 = 0; + 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 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); + + 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 new file mode 100644 index 000000000..d1a30e29b --- /dev/null +++ b/test/algebra/pairing/weierstrass/plonk/mnt4_miller_loop.cpp @@ -0,0 +1,142 @@ +//---------------------------------------------------------------------------// +// 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_mnt4_298_miller_loop + +#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 = 2; + + 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); +} + +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 field_type = typename curve_type::g2_type<>::field_type::base_field_type; + + 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/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.cpp b/test/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.cpp new file mode 100644 index 000000000..581a92cb8 --- /dev/null +++ b/test/algebra/pairing/weierstrass/plonk/mnt6_exponentiation.cpp @@ -0,0 +1,171 @@ +//---------------------------------------------------------------------------// +// 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 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 = { + 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); + + 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 new file mode 100644 index 000000000..c81abdb0b --- /dev/null +++ b/test/algebra/pairing/weierstrass/plonk/mnt6_miller_loop.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_mnt6_298_miller_loop + +#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 = 2; + + 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); +} + +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 field_type = typename curve_type::base_field_type; + + 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 b51a5a369..268e5825d 100644 --- a/test/test_plonk_component.hpp +++ b/test/test_plonk_component.hpp @@ -57,7 +57,7 @@ #include -// #include "profiling_plonk_circuit.hpp" +//#include "profiling_plonk_circuit.hpp" #include #include @@ -253,6 +253,16 @@ namespace nil { } } +#ifdef BLUEPRINT_PLONK_PROFILING_ENABLED + std::ofstream fass("circuit.tbl"); + assignment.export_table(fass); + fass.close(); + + std::ofstream fcirc("circuit.crt"); + bp.export_circuit(fcirc); + fcirc.close(); +#endif + result_check(assignment, component_result); if constexpr (!PrivateInput) { @@ -275,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(); @@ -313,11 +323,12 @@ namespace nil { std::cout << "Usable rows: " << desc.usable_rows_amount << std::endl; std::cout << "Padded rows: " << desc.rows_amount << std::endl; - profiling(assignment); +// 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 +632,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