Skip to content

Commit df85089

Browse files
authored
evmmax: Implement ate pairing for bn254 curve (#852)
Implement optimized version of ecparing precompile in evmone according to [EIP197](https://eips.ethereum.org/EIPS/eip-197) and based on [High-Speed Software Implementation of the Optimal Ate Pairing over Barreto–Naehrig Curves](https://eprint.iacr.org/2010/354.pdf) - Extend the interface with Jacobian caordinates point - Add template struct which implements basic logic for base and extension fields. - Add helper functions needed by the ate pairing. - Implement miller loop and final exponentiation. - Add unit tests and benchmark. - Enable the implementation to be used by state and blockchain tests
2 parents 0175a1f + 328c153 commit df85089

14 files changed

+1334
-79
lines changed

lib/evmone_precompiles/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ target_sources(
1616
bn254.hpp
1717
bn254.cpp
1818
ecc.hpp
19+
pairing/bn254/fields.hpp
20+
pairing/bn254/pairing.cpp
21+
pairing/bn254/utils.hpp
22+
pairing/field_template.hpp
1923
ripemd160.hpp
2024
ripemd160.cpp
2125
secp256k1.hpp

lib/evmone_precompiles/bn254.hpp

+12
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
#pragma once
55

66
#include "ecc.hpp"
7+
#include <optional>
8+
#include <span>
9+
#include <vector>
710

811
namespace evmmax::bn254
912
{
@@ -14,6 +17,7 @@ inline constexpr auto FieldPrime =
1417
0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47_u256;
1518

1619
using Point = ecc::Point<uint256>;
20+
using ExtPoint = ecc::Point<std::pair<uint256, uint256>>;
1721

1822
/// Validates that point is from the bn254 curve group
1923
///
@@ -36,4 +40,12 @@ Point add(const Point& pt1, const Point& pt2) noexcept;
3640
/// Computes [c]P for a point in affine coordinate on the bn254 curve,
3741
Point mul(const Point& pt, const uint256& c) noexcept;
3842

43+
/// ate paring implementation for bn254 curve according to https://eips.ethereum.org/EIPS/eip-197
44+
///
45+
/// @param pairs Sequence of point pairs: a point from the bn254 curve G1 group over the base field
46+
/// followed by a point from twisted curve G2 group over extension field Fq^2.
47+
/// @return `true` when ∏e(vG2[i], vG1[i]) == 1 for i in [0, n] else `false`.
48+
/// std::nullopt on error.
49+
std::optional<bool> pairing_check(std::span<const std::pair<Point, ExtPoint>> pairs) noexcept;
50+
3951
} // namespace evmmax::bn254

lib/evmone_precompiles/ecc.hpp

+36-3
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@ namespace evmmax::ecc
99
{
1010

1111
/// The affine (two coordinates) point on an Elliptic Curve over a prime field.
12-
template <typename IntT>
12+
template <typename ValueT>
1313
struct Point
1414
{
15-
IntT x = 0;
16-
IntT y = 0;
15+
ValueT x = {};
16+
ValueT y = {};
1717

1818
friend constexpr bool operator==(const Point& a, const Point& b) noexcept = default;
1919

20+
friend constexpr Point operator-(const Point& p) noexcept { return {p.x, -p.y}; }
21+
2022
/// Checks if the point represents the special "infinity" value.
2123
[[nodiscard]] constexpr bool is_inf() const noexcept { return *this == Point{}; }
2224
};
@@ -32,10 +34,41 @@ struct ProjPoint
3234

3335
/// Checks if the point represents the special "infinity" value.
3436
[[nodiscard]] constexpr bool is_inf() const noexcept { return x == 0 && z == 0; }
37+
38+
friend constexpr ProjPoint operator-(const ProjPoint& p) noexcept { return {p.x, -p.y, p.z}; }
3539
};
3640

3741
static_assert(ProjPoint<unsigned>{}.is_inf());
3842

43+
// Jacobian (three) coordinates point implementation.
44+
template <typename ValueT>
45+
struct JacPoint
46+
{
47+
ValueT x = 1;
48+
ValueT y = 1;
49+
ValueT z = 0;
50+
51+
// Compares two Jacobian coordinates points
52+
friend constexpr bool operator==(const JacPoint& a, const JacPoint& b) noexcept
53+
{
54+
const auto bz2 = b.z * b.z;
55+
const auto az2 = a.z * a.z;
56+
57+
const auto bz3 = bz2 * b.z;
58+
const auto az3 = az2 * a.z;
59+
60+
return a.x * bz2 == b.x * az2 && a.y * bz3 == b.y * az3;
61+
}
62+
63+
friend constexpr JacPoint operator-(const JacPoint& p) noexcept { return {p.x, -p.y, p.z}; }
64+
65+
// Creates Jacobian coordinates point from affine point
66+
static constexpr JacPoint from(const ecc::Point<ValueT>& ap) noexcept
67+
{
68+
return {ap.x, ap.y, ValueT::one()};
69+
}
70+
};
71+
3972
template <typename IntT>
4073
using InvFn = IntT (*)(const ModArith<IntT>&, const IntT& x) noexcept;
4174

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
// evmone: Fast Ethereum Virtual Machine implementation
2+
// Copyright 2024 The evmone Authors.
3+
// SPDX-License-Identifier: Apache-2.0
4+
#pragma once
5+
6+
#include "../../bn254.hpp"
7+
#include "../../ecc.hpp"
8+
#include "../field_template.hpp"
9+
10+
namespace evmmax::bn254
11+
{
12+
using namespace intx;
13+
14+
/// Specifies base field value type and modular arithmetic for bn254 curve.
15+
struct BaseFieldConfig
16+
{
17+
using ValueT = uint256;
18+
static constexpr auto MOD_ARITH = ModArith{FieldPrime};
19+
static constexpr uint256 ONE = MOD_ARITH.to_mont(1);
20+
};
21+
using Fq = ecc::BaseFieldElem<BaseFieldConfig>;
22+
23+
// Extension fields implemented based on https://hackmd.io/@jpw/bn254#Field-extension-towers
24+
25+
/// Specifies Fq^2 extension field for bn254 curve. Base field extended with irreducible `u^2 + 1`
26+
/// polynomial over the base field. `u` is the Fq^2 element.
27+
struct Fq2Config
28+
{
29+
using BaseFieldT = Fq;
30+
using ValueT = Fq;
31+
static constexpr auto DEGREE = 2;
32+
};
33+
using Fq2 = ecc::ExtFieldElem<Fq2Config>;
34+
35+
/// Specifies Fq^6 extension field for bn254 curve. Fq^2 field extended with irreducible
36+
/// `v^3 - (9 + u)` polynomial over the Fq^2 field. `v` is the Fq^6 field element.
37+
struct Fq6Config
38+
{
39+
using BaseFieldT = Fq;
40+
using ValueT = Fq2;
41+
static constexpr uint8_t DEGREE = 3;
42+
static constexpr auto ksi = Fq2({Fq::from_int(9_u256), Fq::from_int(1_u256)});
43+
static constexpr auto _3_ksi_inv = Fq2({
44+
Fq::from_int(0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5_u256),
45+
Fq::from_int(0x9713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2_u256),
46+
});
47+
};
48+
using Fq6 = ecc::ExtFieldElem<Fq6Config>;
49+
50+
/// Specifies Fq^12 extension field for bn254 curve. Fq^6 field extended with irreducible
51+
/// `w^2 - v` polynomial over the Fq^2 field. `v` is the Fq^6 field element.
52+
/// `w` is Fq^12 field element.
53+
struct Fq12Config
54+
{
55+
using BaseFieldT = Fq;
56+
using ValueT = Fq6;
57+
58+
static constexpr uint8_t DEGREE = 2;
59+
};
60+
using Fq12 = ecc::ExtFieldElem<Fq12Config>;
61+
62+
/// Multiplies two Fq^2 field elements
63+
constexpr Fq2 multiply(const Fq2& a, const Fq2& b)
64+
{
65+
return Fq2({
66+
a.coeffs[0] * b.coeffs[0] - a.coeffs[1] * b.coeffs[1],
67+
a.coeffs[1] * b.coeffs[0] + a.coeffs[0] * b.coeffs[1],
68+
});
69+
}
70+
71+
/// Multiplies two Fq^6 field elements
72+
constexpr Fq6 multiply(const Fq6& a, const Fq6& b)
73+
{
74+
const auto& a0 = a.coeffs[0];
75+
const auto& a1 = a.coeffs[1];
76+
const auto& a2 = a.coeffs[2];
77+
const auto& b0 = b.coeffs[0];
78+
const auto& b1 = b.coeffs[1];
79+
const auto& b2 = b.coeffs[2];
80+
81+
const Fq2& ksi = Fq6Config::ksi;
82+
83+
const auto t0 = a0 * b0;
84+
const auto t1 = a1 * b1;
85+
const auto t2 = a2 * b2;
86+
87+
const auto c0 = ((a1 + a2) * (b1 + b2) - t1 - t2) * ksi + t0;
88+
const auto c1 = (a0 + a1) * (b0 + b1) - t0 - t1 + ksi * t2;
89+
const auto c2 = (a0 + a2) * (b0 + b2) - t0 - t2 + t1;
90+
91+
return Fq6({c0, c1, c2});
92+
}
93+
94+
/// Multiplies two Fq^12 field elements
95+
constexpr Fq12 multiply(const Fq12& a, const Fq12& b)
96+
{
97+
const auto& a0 = a.coeffs[0];
98+
const auto& a1 = a.coeffs[1];
99+
const auto& b0 = b.coeffs[0];
100+
const auto& b1 = b.coeffs[1];
101+
102+
const auto t0 = a0 * b0;
103+
const auto t1 = a1 * b1;
104+
105+
const Fq2& ksi = Fq6Config::ksi;
106+
107+
const auto c0 = t0 + Fq6({ksi * t1.coeffs[2], t1.coeffs[0], t1.coeffs[1]}); // gamma is sparse.
108+
const auto c1 = (a0 + a1) * (b0 + b1) - t0 - t1;
109+
110+
return Fq12({c0, c1});
111+
}
112+
113+
/// Inverses the base field element
114+
inline Fq inverse(const Fq& x)
115+
{
116+
return Fq(field_inv(BaseFieldConfig::MOD_ARITH, x.value()));
117+
}
118+
119+
/// Inverses the Fq^2 field element
120+
inline Fq2 inverse(const Fq2& f)
121+
{
122+
const auto& a0 = f.coeffs[0];
123+
const auto& a1 = f.coeffs[1];
124+
auto t0 = a0 * a0;
125+
auto t1 = a1 * a1;
126+
127+
t0 = t0 + t1;
128+
t1 = t0.inv();
129+
130+
const auto c0 = a0 * t1;
131+
const auto c1 = -(a1 * t1);
132+
133+
return Fq2({c0, c1});
134+
}
135+
136+
/// Inverses the Fq^6 field element
137+
inline Fq6 inverse(const Fq6& f)
138+
{
139+
const auto& a0 = f.coeffs[0];
140+
const auto& a1 = f.coeffs[1];
141+
const auto& a2 = f.coeffs[2];
142+
143+
const Fq2& ksi = Fq6Config::ksi;
144+
145+
const auto t0 = a0 * a0;
146+
const auto t1 = a1 * a1;
147+
const auto t2 = a2 * a2;
148+
149+
const auto t3 = a0 * a1;
150+
const auto t4 = a0 * a2;
151+
const auto t5 = a2 * a1;
152+
153+
const auto c0 = t0 - ksi * t5;
154+
const auto c1 = ksi * t2 - t3;
155+
const auto c2 = t1 - t4;
156+
157+
const auto t = a0 * c0 + (a2 * c1 + a1 * c2) * ksi;
158+
const auto t6 = t.inv();
159+
160+
return Fq6({c0 * t6, c1 * t6, c2 * t6});
161+
}
162+
163+
/// Inverses the Fq^12 field element
164+
inline Fq12 inverse(const Fq12& f)
165+
{
166+
const auto& a0 = f.coeffs[0];
167+
const auto& a1 = f.coeffs[1];
168+
169+
auto t0 = a0 * a0;
170+
auto t1 = a1 * a1;
171+
172+
const Fq2& ksi = Fq6Config::ksi;
173+
174+
t0 = t0 - Fq6({ksi * t1.coeffs[2], t1.coeffs[0], t1.coeffs[1]}); // gamma is sparse.
175+
t1 = t0.inv();
176+
177+
const auto c0 = a0 * t1;
178+
const auto c1 = -(a1 * t1);
179+
180+
return Fq12({c0, c1});
181+
}
182+
} // namespace evmmax::bn254

0 commit comments

Comments
 (0)