Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
hash_utils.test.cpp
Go to the documentation of this file.
3#include <gtest/gtest.h>
4
5using namespace bb;
6using namespace bb::stdlib;
7
11
12namespace {
13constexpr uint64_t MAX_U32 = (1ULL << 32) - 1;
14} // namespace
15
16// Tests constant path: both operands constant, with and without overflow
17TEST(HashUtilsTests, Constants)
18{
20
21 // No overflow
22 field_ct a1 = field_ct(&builder, fr(100));
23 field_ct b1 = field_ct(&builder, fr(200));
24 field_ct r1 = hash_utils::add_normalize_unsafe(a1, b1, /*overflow_bits=*/1);
25 EXPECT_EQ(r1.get_value(), fr(300));
26 EXPECT_TRUE(r1.is_constant());
27
28 // With overflow: (2^32 - 1) + 101 = 2^32 + 100, wraps to 100
29 field_ct a2 = field_ct(&builder, fr(MAX_U32));
30 field_ct b2 = field_ct(&builder, fr(101));
31 field_ct r2 = hash_utils::add_normalize_unsafe(a2, b2, /*overflow_bits=*/1);
32 EXPECT_EQ(r2.get_value(), fr(100));
33 EXPECT_TRUE(r2.is_constant());
34
35 EXPECT_TRUE(CircuitChecker::check(builder));
36}
37
38// Tests witness path with various value combinations and overflow scenarios
39TEST(HashUtilsTests, Witnesses)
40{
42
43 // No overflow
44 field_ct a1 = witness_ct(&builder, fr(1000));
45 field_ct b1 = witness_ct(&builder, fr(2000));
46 field_ct r1 = hash_utils::add_normalize_unsafe(a1, b1, /*overflow_bits=*/1);
47 EXPECT_EQ(r1.get_value(), fr(3000));
48
49 // Overflow by 1: (2^32 - 1) + 1 = 2^32, wraps to 0
50 field_ct a2 = witness_ct(&builder, fr(MAX_U32));
51 field_ct b2 = witness_ct(&builder, fr(1));
52 field_ct r2 = hash_utils::add_normalize_unsafe(a2, b2, /*overflow_bits=*/1);
53 EXPECT_EQ(r2.get_value(), fr(0));
54
55 // Max overflow for two 32-bit values: (2^32-1) + (2^32-1) = 2^33 - 2, wraps to 2^32 - 2
56 field_ct a3 = witness_ct(&builder, fr(MAX_U32));
57 field_ct b3 = witness_ct(&builder, fr(MAX_U32));
58 field_ct r3 = hash_utils::add_normalize_unsafe(a3, b3, /*overflow_bits=*/1);
59 EXPECT_EQ(r3.get_value(), fr(MAX_U32 - 1));
60
61 // Zero handling
62 field_ct a4 = witness_ct(&builder, fr(0));
63 field_ct b4 = witness_ct(&builder, fr(12345));
64 field_ct r4 = hash_utils::add_normalize_unsafe(a4, b4, /*overflow_bits=*/1);
65 EXPECT_EQ(r4.get_value(), fr(12345));
66
67 EXPECT_TRUE(CircuitChecker::check(builder));
68}
69
70// Tests mixed constant/witness - ensures context is properly obtained from either operand
71TEST(HashUtilsTests, MixedConstantWitness)
72{
74
75 // Witness first, constant second
76 field_ct a1 = witness_ct(&builder, fr(500));
77 field_ct b1 = field_ct(&builder, fr(700));
78 field_ct r1 = hash_utils::add_normalize_unsafe(a1, b1, /*overflow_bits=*/1);
79 EXPECT_EQ(r1.get_value(), fr(1200));
80
81 // Constant first (no context), witness second
82 field_ct a2 = field_ct(fr(100));
83 field_ct b2 = witness_ct(&builder, fr(200));
84 field_ct r2 = hash_utils::add_normalize_unsafe(a2, b2, /*overflow_bits=*/1);
85 EXPECT_EQ(r2.get_value(), fr(300));
86
87 EXPECT_TRUE(CircuitChecker::check(builder));
88}
89
90// Tests multi-bit overflow as used in SHA-256/Blake2s where sums can accumulate
91TEST(HashUtilsTests, MultiBitOverflow)
92{
94
95 // Simulate accumulated sum scenario needing >1 overflow bit
96 // Two 33-bit values: sum needs 2 overflow bits
97 uint64_t large_val = (1ULL << 33) - 1;
98 field_ct a = witness_ct(&builder, fr(large_val));
99 field_ct b = witness_ct(&builder, fr(large_val));
100
101 field_ct result = hash_utils::add_normalize_unsafe(a, b, /*overflow_bits=*/3);
102
103 uint64_t expected = static_cast<uint32_t>((2 * large_val) & MAX_U32);
104 EXPECT_EQ(result.get_value(), fr(expected));
105 EXPECT_TRUE(CircuitChecker::check(builder));
106}
107
108// Tests chained operations as used in hash compression functions
109TEST(HashUtilsTests, ChainedOperations)
110{
112
113 field_ct a = witness_ct(&builder, fr(MAX_U32));
114 field_ct b = witness_ct(&builder, fr(MAX_U32));
115 field_ct c = witness_ct(&builder, fr(MAX_U32));
116 field_ct d = witness_ct(&builder, fr(MAX_U32));
117
118 field_ct sum1 = hash_utils::add_normalize_unsafe(a, b, /*overflow_bits=*/1);
119 field_ct sum2 = hash_utils::add_normalize_unsafe(sum1, c, /*overflow_bits=*/1);
120 field_ct sum3 = hash_utils::add_normalize_unsafe(sum2, d, /*overflow_bits=*/1);
121
122 // 4 * (2^32 - 1) mod 2^32 = -4 mod 2^32 = 2^32 - 4
123 uint64_t expected = (4ULL * MAX_U32) & MAX_U32;
124 EXPECT_EQ(sum3.get_value(), fr(expected));
125 EXPECT_TRUE(CircuitChecker::check(builder));
126}
127
128// Tests that circuit fails when overflow_bits is insufficient for actual overflow
129TEST(HashUtilsTests, InsufficientOverflowBitsFails)
130{
132
133 // Two 33-bit values sum to ~2^34, requiring overflow of ~4 (needs 3 bits)
134 // But we only provide 1 overflow bit, so range constraint should fail
135 uint64_t large_val = (1ULL << 33) - 1;
136 field_ct a = witness_ct(&builder, fr(large_val));
137 field_ct b = witness_ct(&builder, fr(large_val));
138
139 [[maybe_unused]] field_ct result = hash_utils::add_normalize_unsafe(a, b, /*overflow_bits=*/1);
140
141 EXPECT_FALSE(CircuitChecker::check(builder));
142}
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:836
bool is_constant() const
Definition field.hpp:441
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
field_t< Builder > field_ct
witness_t< Builder > witness_ct
field_t< Builder > add_normalize_unsafe(const field_t< Builder > &a, const field_t< Builder > &b, size_t overflow_bits)
Compute (a + b) mod 2^32 with circuit constraints.
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FrParams > fr
Definition fr.hpp:155
UltraCircuitBuilder_< UltraExecutionTraceBlocks > UltraCircuitBuilder
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)