Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bool.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Sergei], commit: 72f52e7bad0fc1e36da575fbc2e6bfa1b1104aec}
3// external_1: { status: Complete, auditors: [@ed25519, @JakubHeba (Spearbit)], commit:
4// b463d7c1c52fec2f4e39acfd21219464b00a39d8}
5// external_2: { status: not started, auditors: [], commit: }
6// =====================
7
8#include "bool.hpp"
9#include "../circuit_builders/circuit_builders.hpp"
13
14using namespace bb;
15
16namespace bb::stdlib {
17
21template <typename Builder>
23 : witness_bool(value)
24{
26}
27
31template <typename Builder>
33 : context(parent_context)
34{
36}
37
45template <typename Builder>
46bool_t<Builder>::bool_t(const witness_t<Builder>& value, const bool& use_range_constraint)
48{
49 BB_ASSERT((value.witness == bb::fr::zero()) || (value.witness == bb::fr::one()),
50 "bool_t: witness value is not 0 or 1");
51 witness_index = value.witness_index;
52
53 if (use_range_constraint) {
54 // Create a range constraint gate
55 context->create_small_range_constraint(witness_index, 1, "bool_t: witness value is not 0 or 1");
56 } else {
57 // Create an arithmetic gate to enforce the relation x^2 = x
58 context->create_bool_gate(witness_index);
59 }
60 witness_bool = (value.witness == bb::fr::one());
61 witness_inverted = false;
67template <typename Builder>
68bool_t<Builder>::bool_t(Builder* parent_context, const bool value)
69 : context(parent_context)
70 , witness_bool(value)
71{
73}
78template <typename Builder>
81 , witness_bool(other.witness_bool)
82 , witness_inverted(other.witness_inverted)
83 , witness_index(other.witness_index)
84 , tag(other.tag)
85{}
86
90template <typename Builder>
93 , witness_bool(other.witness_bool)
94 , witness_inverted(other.witness_inverted)
95 , witness_index(other.witness_index)
96 , tag(other.tag)
97{}
98
103template <typename Builder>
105{
106 BB_ASSERT(witness_index != IS_CONSTANT);
107 bool_t<Builder> result(ctx);
108 result.witness_index = witness_index;
109 const bb::fr value = ctx->get_variable(witness_index);
110 // It does not create a constraint.
111 BB_ASSERT_EQ(value * value - value, 0, "bool_t: creating a witness bool from a non-boolean value");
112 result.witness_bool = (value == 1);
113 result.witness_inverted = false;
114 // Since this is now a witness (not a constant), set the free witness tag
115 // The caller should set the appropriate tag if this element has a known provenance
116 result.set_free_witness_tag();
117 return result;
118}
119
123template <typename Builder> bool_t<Builder>& bool_t<Builder>::operator=(const bool other)
124{
125 context = nullptr;
126 witness_index = IS_CONSTANT;
127 witness_bool = other;
128 witness_inverted = false;
130 return *this;
131}
132
136template <typename Builder> bool_t<Builder>& bool_t<Builder>::operator=(const bool_t& other) = default;
137
141template <typename Builder> bool_t<Builder>& bool_t<Builder>::operator=(bool_t&& other)
142{
143 context = other.context;
144 witness_index = other.witness_index;
145 witness_bool = other.witness_bool;
146 witness_inverted = other.witness_inverted;
147 tag = other.tag;
148 return *this;
149}
154template <typename Builder> bool_t<Builder>& bool_t<Builder>::operator=(const witness_t<Builder>& other)
155{
156 BB_ASSERT((other.witness == bb::fr::one()) || (other.witness == bb::fr::zero()),
157 "bool_t: witness value is not 0 or 1");
158 context = other.context;
159 witness_bool = other.witness == bb::fr::one();
160 witness_index = other.witness_index;
161 witness_inverted = false;
162 // Constrain x := other.witness by the relation x^2 = x
163 context->create_bool_gate(witness_index);
164 set_free_witness_tag();
165 return *this;
166}
167
171template <typename Builder> bool_t<Builder> bool_t<Builder>::operator&(const bool_t& other) const
172{
173 Builder* ctx = validate_context<Builder>(context, other.context);
174 bool_t<Builder> result(ctx);
175 bool left = witness_inverted ^ witness_bool;
176 bool right = other.witness_inverted ^ other.witness_bool;
177 result.witness_bool = left && right;
178
179 BB_ASSERT(ctx || (is_constant() && other.is_constant()));
180 if (!is_constant() && !other.is_constant()) {
182 result.witness_index = ctx->add_variable(value);
183
212 int i_a(static_cast<int>(witness_inverted));
213 int i_b(static_cast<int>(other.witness_inverted));
214
215 fr q_m{ 1 - 2 * i_b - 2 * i_a + 4 * i_a * i_b };
216 fr q_l{ i_b * (1 - 2 * i_a) };
217 fr q_r{ i_a * (1 - 2 * i_b) };
218 fr q_o{ -1 };
219 fr q_c{ i_a * i_b };
220
221 ctx->create_arithmetic_gate(
222 { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c });
223 } else if (!is_constant() && other.is_constant()) {
225 // If rhs is a constant true, the output is determined by the lhs. Otherwise the output is a constant
226 // `false`.
227 result = other.witness_bool ? *this : other;
228
229 } else if (is_constant() && !other.is_constant()) {
230 BB_ASSERT(!witness_inverted);
231 // If lhs is a constant true, the output is determined by the rhs. Otherwise the output is a constant
232 // `false`.
233 result = witness_bool ? other : *this;
234 }
235
236 result.tag = OriginTag(tag, other.tag);
237 return result;
238}
239
243template <typename Builder> bool_t<Builder> bool_t<Builder>::operator|(const bool_t& other) const
244{
245 Builder* ctx = validate_context<Builder>(context, other.context);
246
247 bool_t<Builder> result(ctx);
248
249 BB_ASSERT(ctx || (is_constant() && other.is_constant()));
250
251 result.witness_bool = (witness_bool ^ witness_inverted) | (other.witness_bool ^ other.witness_inverted);
253 if (!is_constant() && !other.is_constant()) {
254 result.witness_index = ctx->add_variable(value);
255 // Let
256 // a := lhs = *this;
257 // b := rhs = other;
258 // The result is given by
259 // a + b - a * b = [-(1 - 2*i_a) * (1 - 2*i_b)] * w_a w_b +
260 // [(1 - 2 * i_a) * (1 - i_b)] * w_a
261 // [(1 - 2 * i_b) * (1 - i_a)] * w_b
262 // [i_a + i_b - i_a * i_b] * 1
263 const int rhs_inverted = static_cast<int>(other.witness_inverted);
264 const int lhs_inverted = static_cast<int>(witness_inverted);
265
266 bb::fr q_m{ -(1 - 2 * rhs_inverted) * (1 - 2 * lhs_inverted) };
267 bb::fr q_l{ (1 - 2 * lhs_inverted) * (1 - rhs_inverted) };
268 bb::fr q_r{ (1 - lhs_inverted) * (1 - 2 * rhs_inverted) };
269 bb::fr q_o{ bb::fr::neg_one() };
270 bb::fr q_c{ rhs_inverted + lhs_inverted - rhs_inverted * lhs_inverted };
271
272 // Let r := a | b;
273 // Constrain
274 // q_m * w_a * w_b + q_l * w_a + q_r * w_b + q_o * r + q_c = 0
275 ctx->create_arithmetic_gate(
276 { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c });
277 } else if (!is_constant() && other.is_constant()) {
278 BB_ASSERT_EQ(other.witness_inverted, false);
279
280 // If we are computing a | b and b is a constant `true`, the result is a constant `true` that does not
281 // depend on `a`.
282 result = other.witness_bool ? other : *this;
283
284 } else if (is_constant() && !other.is_constant()) {
285 // If we are computing a | b and `a` is a constant `true`, the result is a constant `true` that does not
286 // depend on `b`.
287 BB_ASSERT_EQ(witness_inverted, false);
288 result = witness_bool ? *this : other;
289 }
290 result.tag = OriginTag(tag, other.tag);
291 return result;
292}
293
297template <typename Builder> bool_t<Builder> bool_t<Builder>::operator^(const bool_t& other) const
298{
299 Builder* ctx = validate_context<Builder>(context, other.context);
300 bool_t<Builder> result(ctx);
301
302 BB_ASSERT(ctx || (is_constant() && other.is_constant()));
303
304 result.witness_bool = (witness_bool ^ witness_inverted) ^ (other.witness_bool ^ other.witness_inverted);
306
307 if (!is_constant() && !other.is_constant()) {
308 result.witness_index = ctx->add_variable(value);
309 // Let
310 // a := lhs = *this;
311 // b := rhs = other;
312 // The result is given by
313 // a + b - 2 * a * b = [-2 *(1 - 2*i_a) * (1 - 2*i_b)] * w_a w_b +
314 // [(1 - 2 * i_a) * (1 - 2 * i_b)] * w_a
315 // [(1 - 2 * i_b) * (1 - 2 * i_a)] * w_b
316 // [i_a + i_b - 2 * i_a * i_b] * 1]
317 const int rhs_inverted = static_cast<int>(other.witness_inverted);
318 const int lhs_inverted = static_cast<int>(witness_inverted);
319 // Compute the value that's being used in several selectors
320 const int aux_prod = (1 - 2 * rhs_inverted) * (1 - 2 * lhs_inverted);
321
322 bb::fr q_m{ -2 * aux_prod };
323 bb::fr q_l{ aux_prod };
324 bb::fr q_r{ aux_prod };
325 bb::fr q_o{ bb::fr::neg_one() };
326 bb::fr q_c{ lhs_inverted + rhs_inverted - 2 * rhs_inverted * lhs_inverted };
327
328 // Let r := a ^ b;
329 // Constrain
330 // q_m * w_a * w_b + q_l * w_a + q_r * w_b + q_o * r + q_c = 0
331 ctx->create_arithmetic_gate(
332 { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c });
333 } else if (!is_constant() && other.is_constant()) {
334 // witness ^ 1 = !witness
335 BB_ASSERT_EQ(other.witness_inverted, false);
336 result = other.witness_bool ? !*this : *this;
337
338 } else if (is_constant() && !other.is_constant()) {
339 BB_ASSERT_EQ(witness_inverted, false);
340 result = witness_bool ? !other : other;
341 }
342 result.tag = OriginTag(tag, other.tag);
343 return result;
344}
348template <typename Builder> bool_t<Builder> bool_t<Builder>::operator!() const
349{
350 bool_t<Builder> result(*this);
351 if (result.is_constant()) {
352 BB_ASSERT(!witness_inverted);
353 // Negate the value of a constant bool_t element.
354 result.witness_bool = !result.witness_bool;
355 } else {
356 // Negate the `inverted` flag of a witnees bool_t element.
357 result.witness_inverted = !result.witness_inverted;
358 }
359 return result;
360}
361
365template <typename Builder> bool_t<Builder> bool_t<Builder>::operator==(const bool_t& other) const
366{
367 Builder* ctx = validate_context<Builder>(context, other.context);
368 bool_t<Builder> result(ctx);
369 BB_ASSERT(ctx || (is_constant() && other.is_constant()));
370
371 result.witness_bool = (witness_bool ^ witness_inverted) == (other.witness_bool ^ other.witness_inverted);
372 if (!is_constant() && !other.is_constant()) {
373 const bb::fr value = result.witness_bool ? bb::fr::one() : bb::fr::zero();
374
375 result.witness_index = context->add_variable(value);
376
377 // Let
378 // a := lhs = *this;
379 // b := rhs = other;
380 // The result is given by
381 // 1 - a - b + 2 a * b = [2 *(1 - 2*i_a) * (1 - 2*i_b)] * w_a w_b +
382 // [-(1 - 2 * i_a) * (1 - 2 * i_b)] * w_a +
383 // [-(1 - 2 * i_b) * (1 - 2 * i_a)] * w_b +
384 // [1 - i_a - i_b + 2 * i_a * i_b] * 1
385 const int rhs_inverted = static_cast<int>(other.witness_inverted);
386 const int lhs_inverted = static_cast<int>(witness_inverted);
387 // Compute the value that's being used in several selectors
388 const int aux_prod = (1 - 2 * rhs_inverted) * (1 - 2 * lhs_inverted);
389 bb::fr q_m{ 2 * aux_prod };
390 bb::fr q_l{ -aux_prod };
391 bb::fr q_r{ -aux_prod };
392 bb::fr q_o{ bb::fr::neg_one() };
393 bb::fr q_c{ 1 - lhs_inverted - rhs_inverted + 2 * rhs_inverted * lhs_inverted };
394
395 ctx->create_arithmetic_gate(
396 { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c });
397
398 } else if (!is_constant() && (other.is_constant())) {
399 // Compare *this with a constant other. If other == true, then we're checking *this == true. In this case we
400 // propagate *this without adding extra constraints, otherwise (if other = false), we propagate !*this.
401 BB_ASSERT_EQ(other.witness_inverted, false);
402 result = other.witness_bool ? *this : !(*this);
403 } else if (is_constant() && !other.is_constant()) {
404 // Completely analogous to the previous case.
405 BB_ASSERT_EQ(witness_inverted, false);
406 result = witness_bool ? other : !other;
407 }
408
409 result.tag = OriginTag(tag, other.tag);
410 return result;
411}
415template <typename Builder> bool_t<Builder> bool_t<Builder>::operator!=(const bool_t<Builder>& other) const
416{
417 return operator^(other);
418}
419
420template <typename Builder> bool_t<Builder> bool_t<Builder>::operator&&(const bool_t<Builder>& other) const
421{
422 return operator&(other);
423}
424
425template <typename Builder> bool_t<Builder> bool_t<Builder>::operator||(const bool_t<Builder>& other) const
426{
427 return operator|(other);
428}
429
433template <typename Builder> void bool_t<Builder>::assert_equal(const bool_t& rhs, std::string const& msg) const
434{
435 const bool_t lhs = *this;
436 Builder* ctx = validate_context<Builder>(rhs.get_context(), lhs.get_context());
437
438 if (lhs.is_constant() && rhs.is_constant()) {
439 BB_ASSERT_EQ(lhs.get_value(), rhs.get_value(), "bool_t::assert_equal: constants are not equal");
440 } else if (lhs.is_constant()) {
442 // if rhs is inverted, flip the value of the lhs constant
443 const bool lhs_value = rhs.witness_inverted ? !lhs.witness_bool : lhs.witness_bool;
444 ctx->assert_equal_constant(rhs.witness_index, lhs_value, msg);
445 } else if (rhs.is_constant()) {
446 BB_ASSERT(!rhs.witness_inverted);
447 // if lhs is inverted, flip the value of the rhs constant
448 const bool rhs_value = lhs.witness_inverted ? !rhs.witness_bool : rhs.witness_bool;
449 ctx->assert_equal_constant(lhs.witness_index, rhs_value, msg);
450 } else {
451 // Both are witnesses - save original tags and clear them to allow different transcript/free witness sources
452 // (e.g., proving 2 separate properties about same object through 2 different transcripts)
453 const auto lhs_original_tag = lhs.get_origin_tag();
454 const auto rhs_original_tag = rhs.get_origin_tag();
455 auto empty_tag = OriginTag::constant(); // Disable origin checking during intermediate operations
456 lhs.set_origin_tag(empty_tag);
457 rhs.set_origin_tag(empty_tag);
458
459 bool_t left = lhs;
460 bool_t right = rhs;
461 // we need to normalize iff lhs or rhs has an inverted witness (but not both)
462 if (lhs.witness_inverted ^ rhs.witness_inverted) {
463 left = left.normalize();
464 right = right.normalize();
465 }
466 ctx->assert_equal(left.witness_index, right.witness_index, msg);
467
468 // Restore tags
469 lhs.set_origin_tag(lhs_original_tag);
470 rhs.set_origin_tag(rhs_original_tag);
471 }
472}
473
477template <typename Builder>
479 const bool_t& lhs,
480 const bool_t& rhs)
481{
482 if (predicate.is_constant()) {
483 auto result = bool_t(predicate.get_value() ? lhs : rhs);
484 result.set_origin_tag(OriginTag(predicate.get_origin_tag(), lhs.get_origin_tag(), rhs.get_origin_tag()));
485 return result.normalize();
486 }
487
488 bool same = lhs.witness_index == rhs.witness_index;
489 bool witness_same = same && !lhs.is_constant() && (lhs.witness_inverted == rhs.witness_inverted);
490 bool const_same = same && lhs.is_constant() && (lhs.witness_bool == rhs.witness_bool);
491 if (witness_same || const_same) {
492 return lhs.normalize();
493 }
494 // Boolean operations can preserve inverted flags when constants are involved
495 // (e.g., inverted_witness && constant_true returns inverted_witness)
496 return ((predicate && lhs) || (!predicate && rhs)).normalize();
497}
498
502template <typename Builder> bool_t<Builder> bool_t<Builder>::implies(const bool_t<Builder>& other) const
503{
504 // Thanks to negation operator being free, this computation requires at most 1 gate.
505 return (!(*this) || other); // P => Q is equiv. to !P || Q (not(P) or Q).
506}
507
511template <typename Builder> void bool_t<Builder>::must_imply(const bool_t& other, std::string const& msg) const
512{
513 implies(other).assert_equal(true, msg);
514}
515
519template <typename Builder> bool_t<Builder> bool_t<Builder>::implies_both_ways(const bool_t<Builder>& other) const
520{
521 return !((*this) ^ other); // P <=> Q is equiv. to !(P ^ Q) (not(P xor Q)).
522}
523
529template <typename Builder> bool_t<Builder> bool_t<Builder>::normalize() const
530{
531 if (is_constant()) {
532 BB_ASSERT(!witness_inverted);
533 return *this;
534 }
535
536 if (!witness_inverted) {
537 return *this;
538 }
539 // Let a := *this, need to constrain a = a_norm
540 // [1 - 2 i_a] w_a + [-1] w_a_norm + [i_a] = 0
541 // ^ ^ ^
542 // q_l q_o q_c
543 const bool value = witness_bool ^ witness_inverted;
544
545 uint32_t new_witness = context->add_variable(bb::fr{ static_cast<int>(value) });
546
547 const int inverted = static_cast<int>(witness_inverted);
548 bb::fr q_l{ 1 - 2 * inverted };
549 bb::fr q_c{ inverted };
550 bb::fr q_o = bb::fr::neg_one();
551 bb::fr q_m = bb::fr::zero();
552 bb::fr q_r = bb::fr::zero();
553 context->create_arithmetic_gate({ witness_index, context->zero_idx(), new_witness, q_m, q_l, q_r, q_o, q_c });
554
555 witness_index = new_witness;
556 witness_bool = value;
557 witness_inverted = false;
558 return *this;
559}
560
562template class bool_t<bb::MegaCircuitBuilder>;
563
564} // namespace bb::stdlib
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
Implements boolean logic in-circuit.
Definition bool.hpp:60
bool get_value() const
Definition bool.hpp:125
bool is_constant() const
Definition bool.hpp:127
void set_origin_tag(const OriginTag &new_tag) const
Definition bool.hpp:154
bool_t implies(const bool_t &other) const
Implements implication operator in circuit.
Definition bool.cpp:502
bool_t normalize() const
A bool_t element is normalized if witness_inverted == false. For a given *this, output its normalized...
Definition bool.cpp:529
bool_t operator&(const bool_t &other) const
Implements AND in circuit.
Definition bool.cpp:171
void set_free_witness_tag()
Definition bool.hpp:156
bool_t operator!() const
Implements negation in circuit.
Definition bool.cpp:348
static bool_t conditional_assign(const bool_t< Builder > &predicate, const bool_t &lhs, const bool_t &rhs)
Conditionally assign lhs or rhs based on predicate, always returns normalized result.
Definition bool.cpp:478
bool_t operator!=(const bool_t &other) const
Implements the not equal operator in circuit.
Definition bool.cpp:415
Builder * get_context() const
Definition bool.hpp:152
Builder * context
Definition bool.hpp:168
uint32_t witness_index
Index of the witness in the builder's witness vector.
Definition bool.hpp:178
bool_t operator&&(const bool_t &other) const
Definition bool.cpp:420
bool_t(const bool value=false)
Construct a constant bool_t object from a bool value.
Definition bool.cpp:22
bool_t operator||(const bool_t &other) const
Definition bool.cpp:425
void must_imply(const bool_t &other, std::string const &msg="bool_t::must_imply") const
Constrains the (a => b) == true.
Definition bool.cpp:511
bool_t & operator=(const bool other)
Assigns a native bool to bool_t object.
Definition bool.cpp:123
void assert_equal(const bool_t &rhs, std::string const &msg="bool_t::assert_equal") const
Implements copy constraint for bool_t elements.
Definition bool.cpp:433
bool witness_inverted
Definition bool.hpp:170
bool_t operator|(const bool_t &other) const
Implements OR in circuit.
Definition bool.cpp:243
OriginTag tag
Definition bool.hpp:179
static bool_t from_witness_index_unsafe(Builder *ctx, uint32_t witness_index)
Create a bool_t from a witness index that is known to contain a constrained bool value.
Definition bool.cpp:104
bool_t implies_both_ways(const bool_t &other) const
Implements a "double-implication" (<=>), a.k.a "iff", a.k.a. "biconditional".
Definition bool.cpp:519
OriginTag get_origin_tag() const
Definition bool.hpp:155
bool_t operator^(const bool_t &other) const
Implements XOR in circuit.
Definition bool.cpp:297
bool_t operator==(const bool_t &other) const
Implements equality operator in circuit.
Definition bool.cpp:365
StrictMock< MockContext > context
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
This file contains part of the logic for the Origin Tag mechanism that tracks the use of in-circuit p...
static OriginTag constant()
static constexpr field neg_one()
static constexpr field one()
static constexpr field zero()