Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bernstein_yang_inverse.test.cpp
Go to the documentation of this file.
8#include <gtest/gtest.h>
9
10namespace {
11
15
16template <class F> uint256_t to_raw(const F& a)
17{
18 F nonmont = a.from_montgomery_form_reduced();
19 return { nonmont.data[0], nonmont.data[1], nonmont.data[2], nonmont.data[3] };
20}
21
22template <class F> F from_raw(const uint256_t& a)
23{
24 F r{ a.data[0], a.data[1], a.data[2], a.data[3] };
25 r.self_to_montgomery_form();
26 return r;
27}
28
29template <class S, class F> uint256_t by_invert(const F& a)
30{
31 constexpr uint256_t p = F::modulus;
32 constexpr uint64_t p_inv_mod_2k = S::p_inv_mod_2k_from_montgomery_r_inv(F::Params::r_inv);
33 return bb::bernstein_yang::invert_vartime<S>(to_raw(a), p, p_inv_mod_2k);
34}
35
36// Random a, BY result roundtripped through Montgomery must invert a.
37template <class S, class F> void check_inverse_matches_modexp(size_t n)
38{
39 for (size_t i = 0; i < n; ++i) {
40 F a = F::random_element();
41 if (a == F::zero()) {
42 continue;
43 }
44 F got = from_raw<F>(by_invert<S>(a));
45 EXPECT_EQ(got * a, F::one()) << "iteration " << i;
46 }
47}
48
49// Same input through both kernels must produce identical canonical output.
50template <class F> void check_native_matches_wasm(size_t n)
51{
52 for (size_t i = 0; i < n; ++i) {
53 F a = F::random_element();
54 if (a == F::zero()) {
55 continue;
56 }
57 EXPECT_EQ(by_invert<Native>(a), by_invert<Wasm>(a)) << "iteration " << i;
58 }
59}
60
61template <class S, class F> void check_edge_cases()
62{
63 // invert(1) == 1
64 EXPECT_EQ(from_raw<F>(by_invert<S>(F::one())), F::one());
65
66 // invert(-1) == -1
67 F neg_one = -F::one();
68 EXPECT_EQ(from_raw<F>(by_invert<S>(neg_one)), neg_one);
69
70 // a * invert(a) == 1 for small / boundary / sparse inputs
71 F two = F::one() + F::one();
72 F neg_two = -two;
73 F top_limb_only{ 0, 0, 0, 1 }; // raw value 2^192, well below modulus for 254-bit fields
74 top_limb_only.self_to_montgomery_form();
75 for (const F& a : { two, neg_two, top_limb_only }) {
76 F inv = from_raw<F>(by_invert<S>(a));
77 EXPECT_EQ(inv * a, F::one());
78 }
79
80 // Involution: invert(invert(a)) == a, on random samples.
81 for (int i = 0; i < 64; ++i) {
82 F a = F::random_element();
83 if (a == F::zero()) {
84 continue;
85 }
86 F inv = from_raw<F>(by_invert<S>(a));
87 F inv_inv = from_raw<F>(by_invert<S>(inv));
88 EXPECT_EQ(inv_inv, a);
89 }
90}
91
92} // namespace
93
94TEST(Wasm9x29, MatchesModexp_BN254_Fr)
95{
96 check_inverse_matches_modexp<Wasm, bb::fr>(500);
97}
98TEST(Wasm9x29, MatchesModexp_BN254_Fq)
99{
100 check_inverse_matches_modexp<Wasm, bb::fq>(500);
101}
102
103TEST(Native5x64, MatchesModexp_BN254_Fr)
104{
105 check_inverse_matches_modexp<Native, bb::fr>(500);
106}
107TEST(Native5x64, MatchesModexp_BN254_Fq)
108{
109 check_inverse_matches_modexp<Native, bb::fq>(500);
110}
111
112TEST(Wasm9x29, EdgeCases_BN254_Fr)
113{
114 check_edge_cases<Wasm, bb::fr>();
115}
116TEST(Wasm9x29, EdgeCases_BN254_Fq)
117{
118 check_edge_cases<Wasm, bb::fq>();
119}
120TEST(Native5x64, EdgeCases_BN254_Fr)
121{
122 check_edge_cases<Native, bb::fr>();
123}
124TEST(Native5x64, EdgeCases_BN254_Fq)
125{
126 check_edge_cases<Native, bb::fq>();
127}
128
129TEST(BernsteinYang, NativeMatchesWasm_BN254_Fr)
130{
131 check_native_matches_wasm<bb::fr>(500);
132}
133TEST(BernsteinYang, NativeMatchesWasm_BN254_Fq)
134{
135 check_native_matches_wasm<bb::fq>(500);
136}
137
138// 256-bit moduli must keep working through field::invert() via the Fermat
139// fallback (the BY dispatch is gated by `modulus_3 < 2^63`, which excludes
140// secp256k1/r1). Pin that behavior so a future change to the gate gets caught.
141TEST(BernsteinYang, FermatFallback_Secp256k1_Fr)
142{
144 EXPECT_EQ(a * a.invert(), bb::secp256k1::fr::one());
145}
146TEST(BernsteinYang, FermatFallback_Secp256r1_Fr)
147{
149 EXPECT_EQ(a * a.invert(), bb::secp256r1::fr::one());
150}
TEST(Wasm9x29, MatchesModexp_BN254_Fr)
FF a
BB_INLINE constexpr field from_montgomery_form_reduced() const noexcept
static constexpr field one()
constexpr field invert() const noexcept
static field random_element(numeric::RNG *engine=nullptr) noexcept