Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
wnaf.test.cpp
Go to the documentation of this file.
1#include "wnaf.hpp"
2#include "../curves/bn254/fr.hpp"
4#include <gtest/gtest.h>
5
6using namespace bb;
7
8namespace {
10}
11
12// NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays)
13namespace {
14
15void recover_fixed_wnaf(const uint64_t* wnaf, bool skew, uint64_t& hi, uint64_t& lo, size_t wnaf_bits)
16{
17 const size_t wnaf_entries = (127 + wnaf_bits - 1) / wnaf_bits;
18 const uint64_t max_table_index = (1UL << (wnaf_bits - 1)) - 1;
19
20 for (size_t i = 0; i < wnaf_entries; ++i) {
21 uint64_t entry = wnaf[i];
22 uint64_t table_index = entry & 0x7fffffffUL;
23 bool sign = ((entry >> 31) & 1) != 0U;
24 uint64_t point_index_bits = entry >> 32;
25
26 EXPECT_LE(table_index, max_table_index)
27 << "entry " << i << ": table_index " << table_index << " exceeds max " << max_table_index;
28
29 // The most significant digit is always positive by construction (no sign bit is OR'd in).
30 if (i == 0) {
31 EXPECT_FALSE(sign) << "entry 0 (most significant digit) must be positive";
32 }
33
34 // All current callers use point_index=0, so bits 32-63 should be clear.
35 EXPECT_EQ(point_index_bits, 0UL) << "entry " << i << ": unexpected non-zero point_index bits";
36 }
37
38 // Recover the scalar: sum signed odd digits at their positional weights, then subtract skew.
39 uint128_t scalar = 0;
40 for (size_t i = 0; i < wnaf_entries; ++i) {
41 uint64_t entry_formatted = wnaf[i];
42 bool negative = ((entry_formatted >> 31) & 1) != 0U;
43 uint64_t digit = ((entry_formatted & 0x7fffffffUL) << 1) + 1;
44 auto shift = static_cast<uint128_t>(wnaf_bits * (wnaf_entries - 1 - i));
45 if (negative) {
46 scalar -= static_cast<uint128_t>(digit) << shift;
47 } else {
48 scalar += static_cast<uint128_t>(digit) << shift;
49 }
50 }
51 scalar -= static_cast<uint128_t>(skew);
52 hi = static_cast<uint64_t>(scalar >> static_cast<uint128_t>(64));
53 lo = static_cast<uint64_t>(scalar & static_cast<uint128_t>(0xffff'ffff'ffff'ffff));
54}
55} // namespace
56
57TEST(wnaf, GetWnafBitsConstLimbBoundary)
58{
59 // scalar[0] bits 59-63 = 1,0,1,0,1 and scalar[1] bits 0-4 = 1,0,1,0,1
60 // Full bit pattern around the boundary (bit 63 | bit 64):
61 // bit: ...59 60 61 62 63 | 64 65 66 67 68 69...
62 // val: ... 1 0 1 0 1 | 1 0 1 0 1 0...
63 const uint64_t scalar[2] = { 0xA800000000000000ULL, 0x0000000000000015ULL };
64
65 // Window starts at bit 63 — straddles the limb boundary (2 bits from lo, 3 from hi)
66 // bits 63,64,65,66,67 = 1,1,0,1,0 → 1 + 2 + 0 + 8 + 0 = 11
67 EXPECT_EQ((wnaf::get_wnaf_bits_const<5, 63>(scalar)), 11ULL);
68
69 // Window starts at bit 64 — exactly at the hi limb start
70 // bits 64,65,66,67,68 = 1,0,1,0,1 → 1 + 0 + 4 + 0 + 16 = 21
71 EXPECT_EQ((wnaf::get_wnaf_bits_const<5, 64>(scalar)), 21ULL);
72
73 // Window starts at bit 65 — one past the boundary, entirely in hi limb
74 // bits 65,66,67,68,69 = 0,1,0,1,0 → 0 + 2 + 0 + 8 + 0 = 10
75 EXPECT_EQ((wnaf::get_wnaf_bits_const<5, 65>(scalar)), 10ULL);
76}
77
78TEST(wnaf, WnafPowerOfTwo)
79{
80 // Powers of 2 are all even (skew = true) and have a single 1-bit with all lower bits zero,
81 // so every window below the leading bit is even, forcing borrows to cascade through all rounds.
82 auto test_power_of_two_scalar = [](uint64_t lo, uint64_t hi) {
83 uint64_t buffer[2] = { lo, hi };
84 uint64_t wnaf_out[WNAF_SIZE(5)] = { 0 };
85 bool skew = false;
86 wnaf::fixed_wnaf<1, 5>(buffer, wnaf_out, skew, 0);
87 EXPECT_TRUE(skew); // all powers of 2 are even
88 uint64_t recovered_hi = 0;
89 uint64_t recovered_lo = 0;
90 recover_fixed_wnaf(wnaf_out, skew, recovered_hi, recovered_lo, 5);
91 EXPECT_EQ(lo, recovered_lo);
92 EXPECT_EQ(hi, recovered_hi);
93 };
94
95 test_power_of_two_scalar(2ULL, 0ULL); // 2^1: smallest even, borrows cascade through all 26 windows
96 test_power_of_two_scalar(1ULL << 32, 0ULL); // 2^32: mid-lo-limb
97 test_power_of_two_scalar(0ULL, 1ULL); // 2^64: exactly at the limb boundary
98 test_power_of_two_scalar(0ULL, 1ULL << 62); // 2^126: near the 127-bit maximum
99}
100
101TEST(wnaf, WnafZero)
102{
103 uint64_t buffer[2]{ 0, 0 };
104 uint64_t wnaf[WNAF_SIZE(5)] = { 0 };
105 bool skew = false;
106 wnaf::fixed_wnaf<1, 5>(buffer, wnaf, skew, 0);
107 uint64_t recovered_hi = 0;
108 uint64_t recovered_lo = 0;
109 recover_fixed_wnaf(wnaf, skew, recovered_hi, recovered_lo, 5);
110 EXPECT_EQ(recovered_lo, 0UL);
111 EXPECT_EQ(recovered_hi, 0UL);
112 EXPECT_EQ(buffer[0], recovered_lo);
113 EXPECT_EQ(buffer[1], recovered_hi);
114}
115
116TEST(wnaf, WnafTwoBitWindow)
117{
122 constexpr uint32_t window = 2;
123 constexpr uint32_t num_bits = 254;
124 constexpr uint32_t num_quads = (num_bits >> 1) + 1;
125 uint64_t wnaf[num_quads] = { 0 };
126 bool skew = false;
127 wnaf::fixed_wnaf<256, 1, window>(&input.data[0], wnaf, skew, 0);
128
153 uint256_t recovered = 0;
154 uint256_t four_power = (uint256_t(1) << num_bits);
155 for (uint64_t i : wnaf) {
156 int extracted = 2 * (static_cast<int>(i) & 1) + 1;
157 bool sign = (i >> 31) == 0;
158
159 if (sign) {
160 recovered += uint256_t(static_cast<uint64_t>(extracted)) * four_power;
161 } else {
162 recovered -= uint256_t(static_cast<uint64_t>(extracted)) * four_power;
163 }
164 four_power >>= 2;
165 }
166 recovered -= static_cast<uint64_t>(skew);
167 EXPECT_EQ(recovered, input);
168}
169
170TEST(wnaf, WnafFixed)
171{
173 buffer.data[1] &= 0x7fffffffffffffffUL;
174 uint64_t wnaf[WNAF_SIZE(5)] = { 0 };
175 bool skew = false;
176 wnaf::fixed_wnaf<1, 5>(&buffer.data[0], wnaf, skew, 0);
177 uint64_t recovered_hi = 0;
178 uint64_t recovered_lo = 0;
179 recover_fixed_wnaf(wnaf, skew, recovered_hi, recovered_lo, 5);
180 EXPECT_EQ(buffer.data[0], recovered_lo);
181 EXPECT_EQ(buffer.data[1], recovered_hi);
182}
183
184TEST(wnaf, WnafFixedSimpleLo)
185{
186 uint64_t rand_buffer[2]{ 1, 0 };
187 uint64_t wnaf[WNAF_SIZE(5)]{ 0 };
188 bool skew = false;
189 wnaf::fixed_wnaf<1, 5>(rand_buffer, wnaf, skew, 0);
190 uint64_t recovered_hi = 0;
191 uint64_t recovered_lo = 0;
192 recover_fixed_wnaf(wnaf, skew, recovered_hi, recovered_lo, 5);
193 EXPECT_EQ(rand_buffer[0], recovered_lo);
194 EXPECT_EQ(rand_buffer[1], recovered_hi);
195}
196
197TEST(wnaf, WnafFixedSimpleHi)
198{
199 uint64_t rand_buffer[2] = { 0, 1 };
200 uint64_t wnaf[WNAF_SIZE(5)] = { 0 };
201 bool skew = false;
202 wnaf::fixed_wnaf<1, 5>(rand_buffer, wnaf, skew, 0);
203 uint64_t recovered_hi = 0;
204 uint64_t recovered_lo = 0;
205 recover_fixed_wnaf(wnaf, skew, recovered_hi, recovered_lo, 5);
206 EXPECT_EQ(rand_buffer[0], recovered_lo);
207 EXPECT_EQ(rand_buffer[1], recovered_hi);
208}
209
210TEST(wnaf, WnafFixedWithEndoSplit)
211{
213 k.data[3] &= 0x0fffffffffffffffUL;
214
215 fr k1{ 0, 0, 0, 0 };
216 fr k2{ 0, 0, 0, 0 };
217
219 uint64_t wnaf[WNAF_SIZE(5)] = { 0 };
220 uint64_t endo_wnaf[WNAF_SIZE(5)] = { 0 };
221 bool skew = false;
222 bool endo_skew = false;
223 wnaf::fixed_wnaf<1, 5>(&k1.data[0], wnaf, skew, 0);
224 wnaf::fixed_wnaf<1, 5>(&k2.data[0], endo_wnaf, endo_skew, 0);
225
226 fr k1_recovered{ 0, 0, 0, 0 };
227 fr k2_recovered{ 0, 0, 0, 0 };
228
229 recover_fixed_wnaf(wnaf, skew, k1_recovered.data[1], k1_recovered.data[0], 5);
230 recover_fixed_wnaf(endo_wnaf, endo_skew, k2_recovered.data[1], k2_recovered.data[0], 5);
231
232 fr result;
233 fr lambda = fr::cube_root_of_unity();
234 result = k2_recovered * lambda;
235 result = k1_recovered - result;
236
237 EXPECT_EQ(result, k);
238}
239
240// NOLINTEND(cppcoreguidelines-avoid-c-arrays)
virtual uint256_t get_random_uint256()=0
numeric::RNG & engine
std::unique_ptr< uint8_t[]> buffer
Definition engine.cpp:50
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:217
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
unsigned __int128 uint128_t
Definition serialize.hpp:45
static constexpr field cube_root_of_unity()
static void split_into_endomorphism_scalars(const field &k, field &k1, field &k2)
Full-width endomorphism decomposition: k ≡ k1 - k2·λ (mod r). Modifies the field elements k1 and k2.
static field random_element(numeric::RNG *engine=nullptr) noexcept
#define WNAF_SIZE(x)
Definition wnaf.hpp:41