Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions lib/evmone_precompiles/bn254.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#pragma once

#include "ecc.hpp"
#include "pairing/field_template.hpp"
#include <optional>
#include <span>
#include <vector>
Expand Down Expand Up @@ -52,12 +53,29 @@ struct Curve
/// @}
};

using Fq = Curve::Fp;

using AffinePoint = ecc::AffinePoint<Curve>;

/// 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<E2> for symmetry with G1.
using ExtPoint = ecc::Point<std::pair<uint256, uint256>>;
/// 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<Fq2Config>;

/// 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<E2>;

/// Validates that point is from the bn254 curve group
///
Expand Down
19 changes: 1 addition & 18 deletions lib/evmone_precompiles/pairing/bn254/fields.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Fq2Config>;
// 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.
Expand Down Expand Up @@ -51,13 +41,6 @@ struct Fq12Config
};
using Fq12 = ecc::ExtFieldElem<Fq12Config>;

/// 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)
{
Expand Down
21 changes: 6 additions & 15 deletions lib/evmone_precompiles/pairing/bn254/pairing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,28 +136,19 @@ std::optional<bool> pairing_check(std::span<const std::pair<AffinePoint, ExtPoin

for (const auto& [p, q] : pairs)
{
if (!is_field_element(q.x.first) || !is_field_element(q.x.second) ||
!is_field_element(q.y.first) || !is_field_element(q.y.second))
{
return std::nullopt;
}

if (!validate(p))
return std::nullopt;

const auto Q_aff = ecc::AffinePoint<E2>{
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
Expand Down
6 changes: 0 additions & 6 deletions lib/evmone_precompiles/pairing/bn254/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,6 @@ static inline const std::array<std::array<Fq2, 5>, 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<E2>& p)
{
Expand Down
21 changes: 12 additions & 9 deletions test/state/precompiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<const uint8_t, 64>{input_ptr, 64});
namespace bn = evmmax::bn254;
auto p = bn::AffinePoint::from_bytes(std::span<const uint8_t, 64>{input_ptr, 64});
if (!p.has_value()) [[unlikely]]
return {EVMC_PRECOMPILE_FAILURE, 0};

const evmmax::bn254::ExtPoint q{
{intx::be::unsafe::load<intx::uint256>(input_ptr + 96),
intx::be::unsafe::load<intx::uint256>(input_ptr + 64)},
{intx::be::unsafe::load<intx::uint256>(input_ptr + 160),
intx::be::unsafe::load<intx::uint256>(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<const uint8_t, 32>{input_ptr + 96, 32});
const auto qx_imag = bn::Fq::from_bytes(std::span<const uint8_t, 32>{input_ptr + 64, 32});
const auto qy_real = bn::Fq::from_bytes(std::span<const uint8_t, 32>{input_ptr + 160, 32});
const auto qy_imag = bn::Fq::from_bytes(std::span<const uint8_t, 32>{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);
Expand Down
124 changes: 40 additions & 84 deletions test/unittests/evmmax_bn254_pairing_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)}),
};

{
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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
Expand All @@ -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<std::pair<AffinePoint, ExtPoint>> pairs = {{p1, q1}, {p2, q2}};
Expand All @@ -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
Expand All @@ -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<std::pair<AffinePoint, ExtPoint>> pairs = {{p1, q1}, {p2, q2}};
Expand Down