diff --git a/lib/evmone_precompiles/bn254.hpp b/lib/evmone_precompiles/bn254.hpp index b1b435b117..929879ee9a 100644 --- a/lib/evmone_precompiles/bn254.hpp +++ b/lib/evmone_precompiles/bn254.hpp @@ -4,6 +4,7 @@ #pragma once #include "ecc.hpp" +#include "pairing/field_template.hpp" #include #include #include @@ -52,12 +53,29 @@ struct Curve /// @} }; +using Fq = Curve::Fp; + using AffinePoint = ecc::AffinePoint; -/// Note that real part of G2 value goes first and imaginary part is the second. i.e (a + b*i) -/// The pairing check precompile EVM ABI presumes that imaginary part goes first. -/// TODO: Migrate G2 input handling to AffinePoint for symmetry with G1. -using ExtPoint = ecc::Point>; +/// Fq² extension field config: base field extended by the irreducible `u² + 1`. +/// Stays in this namespace so ADL finds multiply()/inverse() (defined in pairing/bn254/fields.hpp). +struct Fq2Config +{ + using BaseFieldT = Fq; + using ValueT = Fq; + static constexpr auto DEGREE = 2; +}; +/// Fq² element with coefficients in (real, imaginary) order. +using Fq2 = ecc::ExtFieldElem; + +/// The BN254 twisted curve E₂: y² = x³ + b/ξ over Fq². G2 lives here. +struct E2 +{ + using Fp = Fq2; + static constexpr auto A = 0; +}; + +using ExtPoint = ecc::AffinePoint; /// Validates that point is from the bn254 curve group /// diff --git a/lib/evmone_precompiles/pairing/bn254/fields.hpp b/lib/evmone_precompiles/pairing/bn254/fields.hpp index e27923d53a..4c96860f2a 100644 --- a/lib/evmone_precompiles/pairing/bn254/fields.hpp +++ b/lib/evmone_precompiles/pairing/bn254/fields.hpp @@ -10,19 +10,9 @@ namespace evmmax::bn254 { using namespace intx; -using Fq = Curve::Fp; // Extension fields implemented based on https://hackmd.io/@jpw/bn254#Field-extension-towers - -/// Specifies Fq^2 extension field for bn254 curve. Base field extended with irreducible `u^2 + 1` -/// polynomial over the base field. `u` is the Fq^2 element. -struct Fq2Config -{ - using BaseFieldT = Fq; - using ValueT = Fq; - static constexpr auto DEGREE = 2; -}; -using Fq2 = ecc::ExtFieldElem; +// Fq, Fq2Config, Fq2, and E2 live in bn254.hpp to be reachable from the precompile boundary. /// Specifies Fq^6 extension field for bn254 curve. Fq^2 field extended with irreducible /// `v^3 - (9 + u)` polynomial over the Fq^2 field. `v` is the Fq^6 field element. @@ -51,13 +41,6 @@ struct Fq12Config }; using Fq12 = ecc::ExtFieldElem; -/// The BN254 twisted curve E₂: y² = x³ + b/ξ over Fq². -struct E2 -{ - using Fp = Fq2; - static constexpr auto A = 0; -}; - /// Multiplies two Fq^2 field elements constexpr Fq2 multiply(const Fq2& a, const Fq2& b) { diff --git a/lib/evmone_precompiles/pairing/bn254/pairing.cpp b/lib/evmone_precompiles/pairing/bn254/pairing.cpp index cc8e995ce6..0b1b3ea554 100644 --- a/lib/evmone_precompiles/pairing/bn254/pairing.cpp +++ b/lib/evmone_precompiles/pairing/bn254/pairing.cpp @@ -136,28 +136,19 @@ std::optional pairing_check(std::span{ - Fq2({Fq(q.x.first), Fq(q.x.second)}), Fq2({Fq(q.y.first), Fq(q.y.second)})}; - - const bool g2_is_inf = Q_aff == 0; + const bool g2_is_inf = q == 0; - // Verify that Q in on curve and in proper subgroup. This subgroup is much smaller than - // group containing all the points from twisted curve over Fq2 field. - if (!g2_is_inf && (!is_on_twisted_curve(Q_aff) || !g2_subgroup_check(Q_aff))) + // G2 must be on the twisted curve and in the small subgroup (Q is part of the full + // twisted-curve group otherwise, which would still satisfy is_on_twisted_curve alone). + if (!g2_is_inf && (!is_on_twisted_curve(q) || !g2_subgroup_check(q))) return std::nullopt; - // If any of the points is infinity it means that miller_loop returns 1. so we can skip it. + // If either point is infinity, miller_loop returns 1, so skip it. if (p != 0 && !g2_is_inf) - f = f * miller_loop(Q_aff, p); + f = f * miller_loop(q, p); } // final exp is calculated on accumulated value diff --git a/lib/evmone_precompiles/pairing/bn254/utils.hpp b/lib/evmone_precompiles/pairing/bn254/utils.hpp index ffafaa6f6b..63140583d5 100644 --- a/lib/evmone_precompiles/pairing/bn254/utils.hpp +++ b/lib/evmone_precompiles/pairing/bn254/utils.hpp @@ -67,12 +67,6 @@ static inline const std::array, 3> FROBENIUS_COEFFS = { }, }; -/// Verifies that value is in the proper prime field. -constexpr bool is_field_element(const uint256& v) -{ - return v < Curve::FIELD_PRIME; -} - /// Verifies that affine point over Fq^2 field is on the twisted curve. constexpr bool is_on_twisted_curve(const evmmax::ecc::AffinePoint& p) { diff --git a/test/state/precompiles.cpp b/test/state/precompiles.cpp index 5f28c052cc..b58bd6ee3c 100644 --- a/test/state/precompiles.cpp +++ b/test/state/precompiles.cpp @@ -550,18 +550,21 @@ ExecutionResult ecpairing_execute(const uint8_t* input, size_t input_size, uint8 pairs.reserve(input_size / PAIR_SIZE); // TODO: may throw std::bad_alloc. for (auto input_ptr = input; input_ptr != input + input_size; input_ptr += PAIR_SIZE) { - const auto p = - evmmax::bn254::AffinePoint::from_bytes(std::span{input_ptr, 64}); + namespace bn = evmmax::bn254; + auto p = bn::AffinePoint::from_bytes(std::span{input_ptr, 64}); if (!p.has_value()) [[unlikely]] return {EVMC_PRECOMPILE_FAILURE, 0}; - const evmmax::bn254::ExtPoint q{ - {intx::be::unsafe::load(input_ptr + 96), - intx::be::unsafe::load(input_ptr + 64)}, - {intx::be::unsafe::load(input_ptr + 160), - intx::be::unsafe::load(input_ptr + 128)}, - }; - pairs.emplace_back(*p, q); + // G2 EVM ABI feeds the imaginary coefficient before the real one for each Fq², + // so swap the offsets when reading into (real, imaginary) order. + const auto qx_real = bn::Fq::from_bytes(std::span{input_ptr + 96, 32}); + const auto qx_imag = bn::Fq::from_bytes(std::span{input_ptr + 64, 32}); + const auto qy_real = bn::Fq::from_bytes(std::span{input_ptr + 160, 32}); + const auto qy_imag = bn::Fq::from_bytes(std::span{input_ptr + 128, 32}); + if (!qx_real || !qx_imag || !qy_real || !qy_imag) [[unlikely]] + return {EVMC_PRECOMPILE_FAILURE, 0}; + bn::ExtPoint q{bn::Fq2({*qx_real, *qx_imag}), bn::Fq2({*qy_real, *qy_imag})}; + pairs.emplace_back(std::move(*p), std::move(q)); } const auto res = evmmax::bn254::pairing_check(pairs); diff --git a/test/unittests/evmmax_bn254_pairing_test.cpp b/test/unittests/evmmax_bn254_pairing_test.cpp index fb5ccb543c..5dc469bcda 100644 --- a/test/unittests/evmmax_bn254_pairing_test.cpp +++ b/test/unittests/evmmax_bn254_pairing_test.cpp @@ -24,39 +24,26 @@ TEST(evmmax, bn254_pairing) }; const auto Q1 = ExtPoint{ - { - 0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678_u256, - 0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7_u256, - }, - { - 0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550_u256, - 0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d_u256, - }, + Fq2({Fq(0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678_u256), + Fq(0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7_u256)}), + Fq2({Fq(0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550_u256), + Fq(0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d_u256)}), }; // -Q1: - const auto nQ1 = - ExtPoint{Q1.x, {Curve::FIELD_PRIME - Q1.y.first, Curve::FIELD_PRIME - Q1.y.second}}; + const auto nQ1 = -Q1; // -Q1 * 16: const auto nQ1_16 = ExtPoint{ - { - 0x14191bd65f51663a1d4ad71d8480c3c3260d598aab6ed95681f773abade7fd7a_u256, - 0x299c79589dfb51fd6925fce3a7fc15c441fdafaa24f0d09b7c443befdddde4e5_u256, - }, - { - 0x1d710ac19a995c6395f33be7f3dcd75e0632a006d196da6b4c9ba78708b6bb78_u256, - 0xcae1001513ae5ddf742aa6dc2f52457d9b14e17765dd74fc098ad06045d434e_u256, - }, + Fq2({Fq(0x14191bd65f51663a1d4ad71d8480c3c3260d598aab6ed95681f773abade7fd7a_u256), + Fq(0x299c79589dfb51fd6925fce3a7fc15c441fdafaa24f0d09b7c443befdddde4e5_u256)}), + Fq2({Fq(0x1d710ac19a995c6395f33be7f3dcd75e0632a006d196da6b4c9ba78708b6bb78_u256), + Fq(0xcae1001513ae5ddf742aa6dc2f52457d9b14e17765dd74fc098ad06045d434e_u256)}), }; // -Q1 * 17: const auto nQ1_17 = ExtPoint{ - { - 0x11eeb08db4fe0df9d7617f11f5f8f488d643510f825f3730ffb038c84c9260fd_u256, - 0x12bf46039aa40a61762bf97b1bb028cebc6d42e46bbbe67f715eda54808b74c4_u256, - }, - { - 0x42b65e62de1fd24534db81fd72e7ee832637948c1c466ccb08171e503f23e72_u256, - 0x197a5efb333448885788690df5af2211c1697dd8b7b1f8845b4e30a909d2b0f5_u256, - }, + Fq2({Fq(0x11eeb08db4fe0df9d7617f11f5f8f488d643510f825f3730ffb038c84c9260fd_u256), + Fq(0x12bf46039aa40a61762bf97b1bb028cebc6d42e46bbbe67f715eda54808b74c4_u256)}), + Fq2({Fq(0x42b65e62de1fd24534db81fd72e7ee832637948c1c466ccb08171e503f23e72_u256), + Fq(0x197a5efb333448885788690df5af2211c1697dd8b7b1f8845b4e30a909d2b0f5_u256)}), }; { @@ -108,27 +95,16 @@ TEST(evmmax, bn254_pairing_invalid_input) 0x22980b2e458ec77e258b19ca3a7b46181f63c6536307acae03eea236f6919eeb_u256, 0x4eab993e2ba2cca2b08c216645e3fbcf80ae67515b2c49806c17b90c9d3cad3_u256, }, - { - { - 0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678_u256, - 0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7_u256, - }, - { - 0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550_u256, - 0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d_u256, - }, + ExtPoint{ + Fq2({Fq(0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678_u256), + Fq(0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7_u256)}), + Fq2({Fq(0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550_u256), + Fq(0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d_u256)}), }, }}; EXPECT_EQ(pairing_check(valid_input), false); - { - // Coordinate not a field element - auto input = valid_input; - input[0].second.x.second = Curve::FIELD_PRIME; - EXPECT_EQ(pairing_check(input), std::nullopt); - } - { // Point P (G1) not on curve auto input = valid_input; @@ -139,21 +115,17 @@ TEST(evmmax, bn254_pairing_invalid_input) { // Point Q (G2) not on curve auto input = valid_input; - input[0].second.x.first += 1; + input[0].second.x.coeffs[0] += Fq{1}; EXPECT_EQ(pairing_check(input), std::nullopt); } { - // Q not in proper group. Q id a member of small subgroup on twisted curve over Fq^2 + // Q not in proper group. Q is a member of small subgroup on twisted curve over Fq^2. const ExtPoint Q{ - { - 0x13d841ba7ff3c6efd6870c3fea13a3ecab0423af5e4db9c5d28a6b46a05cd57b_u256, - 0x1a2b1eaa7b20faae36d26eff4db6e336c34434b66eded3cc5303d51ae353f478_u256, - }, - { - 0x2d3e8808aa7a7fffa8f871f10df8d59c6dd725889c46e9136e01cb2465b20723_u256, - 0x1d5224817b8714531fc77e20b975178b1b3044f4b729fa3230db03dc0088ebdb_u256, - }, + Fq2({Fq(0x13d841ba7ff3c6efd6870c3fea13a3ecab0423af5e4db9c5d28a6b46a05cd57b_u256), + Fq(0x1a2b1eaa7b20faae36d26eff4db6e336c34434b66eded3cc5303d51ae353f478_u256)}), + Fq2({Fq(0x2d3e8808aa7a7fffa8f871f10df8d59c6dd725889c46e9136e01cb2465b20723_u256), + Fq(0x1d5224817b8714531fc77e20b975178b1b3044f4b729fa3230db03dc0088ebdb_u256)}), }; auto input = valid_input; @@ -171,14 +143,10 @@ TEST(evmmax_bn254, evm_codes_example) 0x2c0f001f52110ccfe69108924926e45f0b0c868df0e7bde1fe16d3242dc715f6_u256, }; const auto q1 = ExtPoint{ - { - 0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9_u256, - 0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc_u256, - }, - { - 0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e_u256, - 0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90_u256, - }, + Fq2({Fq(0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9_u256), + Fq(0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc_u256)}), + Fq2({Fq(0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e_u256), + Fq(0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90_u256)}), }; // Pair 2 @@ -187,14 +155,10 @@ TEST(evmmax_bn254, evm_codes_example) 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45_u256, }; const auto q2 = ExtPoint{ - { - 0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7_u256, - 0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4_u256, - }, - { - 0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc_u256, - 0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2_u256, - }, + Fq2({Fq(0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7_u256), + Fq(0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4_u256)}), + Fq2({Fq(0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc_u256), + Fq(0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2_u256)}), }; const std::vector> pairs = {{p1, q1}, {p2, q2}}; @@ -212,14 +176,10 @@ TEST(evmmax_bn254, evm_codes_example_changed_order) 0x2c0f001f52110ccfe69108924926e45f0b0c868df0e7bde1fe16d3242dc715f6_u256, }; const auto q1 = ExtPoint{ - { - 0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc_u256, - 0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9_u256, - }, - { - 0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90_u256, - 0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e_u256, - }, + Fq2({Fq(0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc_u256), + Fq(0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9_u256)}), + Fq2({Fq(0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90_u256), + Fq(0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e_u256)}), }; // Pair 2 @@ -228,14 +188,10 @@ TEST(evmmax_bn254, evm_codes_example_changed_order) 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45_u256, }; const auto q2 = ExtPoint{ - { - 0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4_u256, - 0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7_u256, - }, - { - 0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2_u256, - 0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc_u256, - }, + Fq2({Fq(0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4_u256), + Fq(0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7_u256)}), + Fq2({Fq(0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2_u256), + Fq(0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc_u256)}), }; const std::vector> pairs = {{p1, q1}, {p2, q2}};