Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
pedersen.test.cpp
Go to the documentation of this file.
9#include "pedersen.hpp"
10
11using namespace bb;
13namespace {
15}
16
17template <typename Builder> class StdlibPedersen : public testing::Test {
19
21 using fr_ct = typename _curve::ScalarField;
26
27 public:
28 static void test_pedersen_two()
29 {
30
32
33 fr left_in = fr::random_element();
34 fr right_in = fr::random_element();
35
36 // ensure left has skew 1, right has skew 0
37 if ((left_in.from_montgomery_form().data[0] & 1) == 1) {
38 left_in += fr::one();
39 }
40 if ((right_in.from_montgomery_form().data[0] & 1) == 0) {
41 right_in += fr::one();
42 }
43
44 fr_ct left = public_witness_ct(&builder, left_in);
45 fr_ct right = witness_ct(&builder, right_in);
46
47 builder.fix_witness(left.witness_index, left.get_value());
48 builder.fix_witness(right.witness_index, right.get_value());
49
50 fr_ct out = pedersen_hash::hash({ left, right });
51
52 fr hash_native = crypto::pedersen_hash::hash({ left.get_value(), right.get_value() });
53 EXPECT_EQ(out.get_value(), hash_native);
54
55 check_circuit_and_gate_count(builder, 2897);
56 }
57
59 {
61
62 fr zero_fr = fr::zero();
63 fr one_fr = fr::one();
64 fr r_minus_one_fr = fr::modulus - 1;
65 fr r_minus_two_fr = fr::modulus - 2;
66 fr r_fr = fr::modulus;
67
68 fr_ct zero = witness_ct(&builder, zero_fr);
69 fr_ct one = witness_ct(&builder, one_fr);
70 fr_ct r_minus_one = witness_ct(&builder, r_minus_one_fr);
71 fr_ct r_minus_two = witness_ct(&builder, r_minus_two_fr);
72 fr_ct r = witness_ct(&builder, r_fr);
73
74 fr_ct out_1_with_zero = pedersen_hash::hash({ zero, one });
75 fr_ct out_1_with_r = pedersen_hash::hash({ r, one });
76 fr_ct out_2 = pedersen_hash::hash({ r_minus_one, r_minus_two });
77 fr_ct out_with_zero = pedersen_hash::hash({ out_1_with_zero, out_2 });
78 fr_ct out_with_r = pedersen_hash::hash({ out_1_with_r, out_2 });
79
80 EXPECT_EQ(bool(out_1_with_zero.get_value() == out_1_with_r.get_value()), true);
81
82 fr hash_native_1_with_zero = crypto::pedersen_hash::hash({ zero.get_value(), one.get_value() });
83 fr hash_native_1_with_r = crypto::pedersen_hash::hash({ r.get_value(), one.get_value() });
84 fr hash_native_2 = crypto::pedersen_hash::hash({ r_minus_one.get_value(), r_minus_two.get_value() });
85 fr hash_native_with_zero = crypto::pedersen_hash::hash({ out_1_with_zero.get_value(), out_2.get_value() });
86 fr hash_native_with_r = crypto::pedersen_hash::hash({ out_1_with_r.get_value(), out_2.get_value() });
87
88 EXPECT_EQ(out_1_with_zero.get_value(), hash_native_1_with_zero);
89 EXPECT_EQ(out_1_with_r.get_value(), hash_native_1_with_r);
90 EXPECT_EQ(out_2.get_value(), hash_native_2);
91 EXPECT_EQ(out_with_zero.get_value(), hash_native_with_zero);
92 EXPECT_EQ(out_with_r.get_value(), hash_native_with_r);
93 EXPECT_EQ(hash_native_with_zero, hash_native_with_r);
94
95 check_circuit_and_gate_count(builder, 3482);
96 }
97
98 static void test_pedersen_large()
99 {
101
102 fr left_in = fr::random_element();
103 fr right_in = fr::random_element();
104 // ensure left has skew 1, right has skew 0
105 if ((left_in.from_montgomery_form().data[0] & 1) == 1) {
106 left_in += fr::one();
107 }
108 if ((right_in.from_montgomery_form().data[0] & 1) == 0) {
109 right_in += fr::one();
110 }
111 fr_ct left = witness_ct(&builder, left_in);
112 fr_ct right = witness_ct(&builder, right_in);
113
114 for (size_t i = 0; i < 256; ++i) {
115 left = pedersen_hash::hash({ left, right });
116 }
117
118 builder.set_public_input(left.witness_index);
119
120 check_circuit_and_gate_count(builder, 40379);
121 }
122
123 static void test_multi_hash()
124 {
126
127 for (size_t i = 0; i < 7; ++i) {
128 std::vector<fr> inputs;
129 inputs.push_back(bb::fr::random_element());
130 inputs.push_back(bb::fr::random_element());
131 inputs.push_back(bb::fr::random_element());
132 inputs.push_back(bb::fr::random_element());
133
134 if (i == 1) {
135 inputs[0] = fr(0);
136 }
137 if (i == 2) {
138 inputs[1] = fr(0);
139 inputs[2] = fr(0);
140 }
141 if (i == 3) {
142 inputs[3] = fr(0);
143 }
144 if (i == 4) {
145 inputs[0] = fr(0);
146 inputs[3] = fr(0);
147 }
148 if (i == 5) {
149 inputs[0] = fr(0);
150 inputs[1] = fr(0);
151 inputs[2] = fr(0);
152 inputs[3] = fr(0);
153 }
154 if (i == 6) {
155 inputs[1] = fr(1);
156 }
157 std::vector<fr_ct> witnesses;
158 for (auto input : inputs) {
159 witnesses.push_back(witness_ct(&builder, input));
160 }
161
162 fr expected = crypto::pedersen_hash::hash(inputs);
163
164 fr_ct result = pedersen_hash::hash(witnesses);
165 EXPECT_EQ(result.get_value(), expected);
166 }
167
168 // Note: gate count delta is an illusion due to extra constants added by default in the Mega builder which then
169 // get resused as ROM indices in the underlying batch mul algorithm (only applies for num_inputs > 2).
171 check_circuit_and_gate_count(builder, 9724);
172 } else {
173 check_circuit_and_gate_count(builder, 9721);
174 }
175 }
176
177 static void test_hash_eight()
178 {
180
182 inputs.reserve(8);
184
185 for (size_t i = 0; i < 8; ++i) {
186 inputs.emplace_back(bb::fr::random_element());
187 witness_inputs.emplace_back(witness_ct(&builder, inputs[i]));
188 }
189
190 constexpr size_t hash_idx = 10;
191 grumpkin::fq expected = crypto::pedersen_hash::hash(inputs, hash_idx);
192 auto result = pedersen_hash::hash(witness_inputs, hash_idx);
193
194 EXPECT_EQ(result.get_value(), expected);
195
196 // Note: gate count delta is an illusion due to extra constants added by default in the Mega builder which then
197 // get resused as ROM indices in the underlying batch mul algorithm (only applies for num_inputs > 2).
199 check_circuit_and_gate_count(builder, 5417);
200 } else {
201 check_circuit_and_gate_count(builder, 5414);
202 }
203 }
204
206 {
208
209 std::vector<fr> inputs;
211
212 for (size_t i = 0; i < 8; ++i) {
213 inputs.push_back(bb::fr::random_element());
214 if (i % 2 == 1) {
215 witness_inputs.push_back(witness_ct(&builder, inputs[i]));
216 } else {
217 witness_inputs.push_back(fr_ct(&builder, inputs[i]));
218 }
219 }
220
221 fr expected = crypto::pedersen_hash::hash(inputs);
222 auto result = pedersen_hash::hash(witness_inputs);
223
224 EXPECT_EQ(result.get_value(), expected);
225
226 // Note: gate count delta is an illusion due to extra constants added by default in the Mega builder which then
227 // get resused as ROM indices in the underlying batch mul algorithm (only applies for num_inputs > 2).
229 check_circuit_and_gate_count(builder, 3997);
230 } else {
231 check_circuit_and_gate_count(builder, 3994);
232 }
233 }
234
235 static void test_empty_input()
236 {
238 std::vector<fr> empty_inputs_native;
239 std::vector<fr_ct> empty_inputs;
240
241 [[maybe_unused]] auto native_result = crypto::pedersen_hash::hash(empty_inputs_native);
242 auto stdlib_result = pedersen_hash::hash(empty_inputs);
243
244 EXPECT_EQ(stdlib_result.get_value(), fr::zero());
245
246 // NOTE: Empty input handling differs between native and stdlib implementations because the representation of
247 // the point at infinity differs
248 // EXPECT_NE(stdlib_result.get_value(), native_result); // They are different!
249
250 check_circuit_and_gate_count(builder, 0); // Empty input returns 0
251 }
252
253 static void test_single_input()
254 {
256
258 fr_ct witness = witness_ct(&builder, value);
259
260 auto result = pedersen_hash::hash({ witness });
261 auto expected = crypto::pedersen_hash::hash({ value });
262
263 EXPECT_EQ(result.get_value(), expected);
264
265 check_circuit_and_gate_count(builder, 2823);
266 }
267
268 static void test_large_inputs()
269 {
271 std::vector<fr> native_inputs;
272 std::vector<fr_ct> witness_inputs;
273
274 constexpr size_t size = 200;
275 for (size_t i = 0; i < size; ++i) {
276 native_inputs.push_back(fr::random_element());
277 witness_inputs.push_back(witness_ct(&builder, native_inputs.back()));
278 }
279
280 auto result = pedersen_hash::hash(witness_inputs);
281 auto expected = crypto::pedersen_hash::hash(native_inputs);
282
283 EXPECT_EQ(result.get_value(), expected);
284
285 // Note: gate count delta is an illusion due to extra constants added by default in the Mega builder which then
286 // get resused as ROM indices in the underlying batch mul algorithm (only applies for num_inputs > 2).
288 check_circuit_and_gate_count(builder, 62376);
289 } else {
290 check_circuit_and_gate_count(builder, 62373);
291 }
292 }
293
295 {
296 using GeneratorContext = typename crypto::GeneratorContext<typename cycle_group::Curve>;
297
299
300 std::vector<fr> inputs;
301 std::vector<fr_ct> witness_inputs;
302
303 for (size_t i = 0; i < 5; ++i) {
304 inputs.push_back(fr::random_element());
305 witness_inputs.push_back(witness_ct(&builder, inputs.back()));
306 }
307
308 // Test 1: Implicit conversion from size_t to GeneratorContext
309 // When passing a size_t, it becomes GeneratorContext(offset) with default domain separator
310 for (size_t hash_idx : { size_t(0), size_t(1), size_t(10), size_t(100), size_t(1000) }) {
311 // This implicitly creates GeneratorContext(hash_idx)
312 GeneratorContext ctx{ hash_idx }; // For native comparison
313 auto result = pedersen_hash::hash(witness_inputs, ctx);
314 auto expected = crypto::pedersen_hash::hash(inputs, ctx);
315
316 EXPECT_EQ(result.get_value(), expected);
317 }
318
319 // Test 2: Verify that different offsets produce different results
320 auto result_offset_0 = pedersen_hash::hash(witness_inputs, 0);
321 auto result_offset_1 = pedersen_hash::hash(witness_inputs, 1);
322 EXPECT_NE(result_offset_0.get_value(), result_offset_1.get_value());
323
324 // Test 3: Explicit GeneratorContext with custom domain separators
325 // Different domain separators should produce different generators and thus different hashes
326 GeneratorContext ctx_domain_a(0, "domain_a");
327 GeneratorContext ctx_domain_b(0, "domain_b");
328 GeneratorContext ctx_default(0); // Uses default domain separator
329
330 auto result_domain_a = pedersen_hash::hash(witness_inputs, ctx_domain_a);
331 auto result_domain_b = pedersen_hash::hash(witness_inputs, ctx_domain_b);
332 auto result_default = pedersen_hash::hash(witness_inputs, ctx_default);
333
334 // Verify native implementation matches
335 auto expected_domain_a = crypto::pedersen_hash::hash(inputs, ctx_domain_a);
336 auto expected_domain_b = crypto::pedersen_hash::hash(inputs, ctx_domain_b);
337 auto expected_default = crypto::pedersen_hash::hash(inputs, ctx_default);
338
339 EXPECT_EQ(result_domain_a.get_value(), expected_domain_a);
340 EXPECT_EQ(result_domain_b.get_value(), expected_domain_b);
341 EXPECT_EQ(result_default.get_value(), expected_default);
342
343 // Different domain separators should produce different results
344 EXPECT_NE(result_domain_a.get_value(), result_domain_b.get_value());
345 EXPECT_NE(result_domain_a.get_value(), result_default.get_value());
346 EXPECT_NE(result_domain_b.get_value(), result_default.get_value());
347
348 // Test 4: Same domain separator with different offsets
349 GeneratorContext ctx_offset_10(10, "domain_test");
350 GeneratorContext ctx_offset_20(20, "domain_test");
351
352 auto result_offset_10 = pedersen_hash::hash(witness_inputs, ctx_offset_10);
353 auto result_offset_20 = pedersen_hash::hash(witness_inputs, ctx_offset_20);
354
355 // Different offsets with same domain should produce different results
356 EXPECT_NE(result_offset_10.get_value(), result_offset_20.get_value());
357
358 // Note: gate count delta is an illusion due to extra constants added by default in the Mega builder which then
359 // get resused as ROM indices in the underlying batch mul algorithm (only applies for num_inputs > 2).
361 check_circuit_and_gate_count(builder, 21845);
362 } else {
363 check_circuit_and_gate_count(builder, 21842);
364 }
365 }
366
367 static void test_determinism()
368 {
370
371 std::vector<fr> inputs;
372 std::vector<fr_ct> witness_inputs;
373
374 for (size_t i = 0; i < 5; ++i) {
375 inputs.push_back(fr::random_element());
376 witness_inputs.push_back(witness_ct(&builder, inputs.back()));
377 }
378
379 // Hash the same inputs multiple times
380 auto result1 = pedersen_hash::hash(witness_inputs);
381 auto result2 = pedersen_hash::hash(witness_inputs);
382 auto result3 = pedersen_hash::hash(witness_inputs);
383
384 // All should produce the same result
385 EXPECT_EQ(result1.get_value(), result2.get_value());
386 EXPECT_EQ(result2.get_value(), result3.get_value());
387
388 // Also verify against native implementation
389 auto expected = crypto::pedersen_hash::hash(inputs);
390 EXPECT_EQ(result1.get_value(), expected);
391
392 // Note: gate count delta is an illusion due to extra constants added by default in the Mega builder which then
393 // get resused as ROM indices in the underlying batch mul algorithm (only applies for num_inputs > 2).
395 check_circuit_and_gate_count(builder, 6645);
396 } else {
397 check_circuit_and_gate_count(builder, 6642);
398 }
399 }
400};
401
402using CircuitTypes = testing::Types<bb::UltraCircuitBuilder, bb::MegaCircuitBuilder>;
403
405
407{
408 using Builder = TypeParam;
411 auto builder = Builder();
412
413 const size_t num_inputs = 10;
414
416 std::vector<fr> inputs_native;
417
418 for (size_t i = 0; i < num_inputs; ++i) {
419 const auto element = fr::random_element(&engine);
420 inputs_native.emplace_back(element);
421 inputs.emplace_back(field_ct(witness_ct(&builder, element)));
422 }
423
424 auto result = stdlib::pedersen_hash<Builder>::hash(inputs);
425 auto expected = crypto::pedersen_hash::hash(inputs_native);
426
427 EXPECT_EQ(result.get_value(), expected);
428
429 // Note: gate count delta is an illusion due to extra constants added by default in the Mega builder which then
430 // get resused as ROM indices in the underlying batch mul algorithm (only applies for num_inputs > 2).
432 check_circuit_and_gate_count(builder, 5565);
433 } else {
434 check_circuit_and_gate_count(builder, 5562);
435 }
436}
437
439{
440 TestFixture::test_pedersen_two();
441};
442
444{
445 TestFixture::test_pedersen_edge_cases();
446};
447
449{
450 TestFixture::test_pedersen_large();
451};
452
454{
455 TestFixture::test_multi_hash();
456};
457
459{
460 TestFixture::test_hash_eight();
461};
462
464{
465 TestFixture::test_hash_constants();
466};
467
469{
470 TestFixture::test_empty_input();
471};
472
474{
475 TestFixture::test_single_input();
476};
477
479{
480 TestFixture::test_large_inputs();
481};
482
483TYPED_TEST(StdlibPedersen, GeneratorContexts)
484{
485 TestFixture::test_generator_contexts();
486};
487
489{
490 TestFixture::test_determinism();
491};
static void test_pedersen_two()
static void test_pedersen_edge_cases()
typename stdlib::cycle_group< Builder > cycle_group
static void test_single_input()
typename _curve::witness_ct witness_ct
static void test_large_inputs()
static void test_hash_eight()
static void test_hash_constants()
static void test_empty_input()
typename _curve::ScalarField fr_ct
static void test_pedersen_large()
typename _curve::public_witness_ct public_witness_ct
typename stdlib::pedersen_hash< Builder > pedersen_hash
static void test_determinism()
static void test_generator_contexts()
static void test_multi_hash()
typename _curve::byte_array_ct byte_array_ct
static Fq hash(const std::vector< Fq > &inputs, GeneratorContext context={})
Given a vector of fields, generate a pedersen hash using generators from context.
Definition pedersen.cpp:78
cycle_group represents a group Element of the proving system's embedded curve, i.e....
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:829
uint32_t witness_index
Definition field.hpp:132
stdlib class that evaluates in-circuit pedersen hashes, consistent with behavior in crypto::pedersen_...
Definition pedersen.hpp:23
static field_ct hash(const std::vector< field_ct > &in, GeneratorContext context={})
Computes a pedersen hash of the provided inputs.
Definition pedersen.cpp:29
AluTraceBuilder builder
Definition alu.test.cpp:123
ECCVMCircuitBuilder Builder
bn254::witness_ct witness_ct
stdlib::field_t< Builder > field_ct
testing::Types< bb::UltraCircuitBuilder, bb::MegaCircuitBuilder > CircuitTypes
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:190
void check_circuit_and_gate_count(Builder &builder, uint32_t expected_gates_without_base)
Utility function for gate count checking and circuit verification.
Entry point for Barretenberg command-line interface.
TYPED_TEST_SUITE(ShpleminiTest, TestSettings)
field< Bn254FrParams > fr
Definition fr.hpp:174
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static constexpr field one()
static constexpr uint256_t modulus
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_INLINE constexpr field from_montgomery_form() const noexcept
static constexpr field zero()
field_t< CircuitBuilder > ScalarField
Definition bn254.hpp:33
byte_array< CircuitBuilder > byte_array_ct
Definition bn254.hpp:43
public_witness_t< CircuitBuilder > public_witness_ct
Definition bn254.hpp:42
witness_t< CircuitBuilder > witness_ct
Definition bn254.hpp:41
#define HEAVY_TYPED_TEST(x, y)
Definition test.hpp:11