From 6e11737f1591522187736315554f82abac470bb9 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Thu, 12 Jun 2025 14:29:57 +0000 Subject: [PATCH 01/18] constructors + is constant clean up --- .../stdlib/primitives/bool/bool.cpp | 82 ++++++++----------- 1 file changed, 36 insertions(+), 46 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp index 522c2c4a0848..b74d688928ef 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp @@ -16,18 +16,13 @@ template bool_t::bool_t(const bool value) : context(nullptr) , witness_bool(value) - , witness_inverted(false) , witness_index(IS_CONSTANT) {} template bool_t::bool_t(Builder* parent_context) : context(parent_context) -{ - witness_bool = false; - witness_inverted = false; - witness_index = IS_CONSTANT; -} +{} template bool_t::bool_t(const witness_t& value) @@ -44,32 +39,27 @@ bool_t::bool_t(const witness_t& value) template bool_t::bool_t(Builder* parent_context, const bool value) : context(parent_context) -{ - context = parent_context; - witness_index = IS_CONSTANT; - witness_bool = value; - witness_inverted = false; -} + , witness_bool(value) +{} template bool_t::bool_t(const bool_t& other) : context(other.context) -{ - witness_index = other.witness_index; - witness_bool = other.witness_bool; - witness_inverted = other.witness_inverted; - tag = other.tag; -} + , witness_bool(other.witness_bool) + , witness_inverted(other.witness_inverted) + , witness_index(other.witness_index) + , tag(other.tag) +{} template bool_t::bool_t(bool_t&& other) : context(other.context) -{ - witness_index = other.witness_index; - witness_bool = other.witness_bool; - witness_inverted = other.witness_inverted; - tag = other.tag; -} + , witness_bool(other.witness_bool) + , witness_inverted(other.witness_inverted) + , witness_index(other.witness_index) + , tag(other.tag) + +{} template bool_t& bool_t::operator=(const bool other) { @@ -104,7 +94,7 @@ template bool_t& bool_t::operator=(const wi { ASSERT((other.witness == bb::fr::one()) || (other.witness == bb::fr::zero())); context = other.context; - witness_bool = (other.witness == bb::fr::zero()) ? false : true; + witness_bool = other.witness == bb::fr::one(); witness_index = other.witness_index; witness_inverted = false; context->create_bool_gate(witness_index); @@ -114,12 +104,12 @@ template bool_t& bool_t::operator=(const wi template bool_t bool_t::operator&(const bool_t& other) const { - bool_t result(context == nullptr ? other.context : context); + bool_t result(context ? context : other.context); bool left = witness_inverted ^ witness_bool; bool right = other.witness_inverted ^ other.witness_bool; - ASSERT(result.context || (witness_index == IS_CONSTANT && other.witness_index == IS_CONSTANT)); - if (witness_index != IS_CONSTANT && other.witness_index != IS_CONSTANT) { + ASSERT(result.context || (is_constant() && other.is_constant())); + if (!is_constant() && !other.is_constant()) { result.witness_bool = left & right; bb::fr value = result.witness_bool ? bb::fr::one() : bb::fr::zero(); result.witness_index = context->add_variable(value); @@ -169,7 +159,7 @@ template bool_t bool_t::operator&(const boo q3, qc, }); - } else if (witness_index != IS_CONSTANT && other.witness_index == IS_CONSTANT) { + } else if (!is_constant() && other.is_constant()) { if (other.witness_bool ^ other.witness_inverted) { result = bool_t(*this); } else { @@ -177,7 +167,7 @@ template bool_t bool_t::operator&(const boo result.witness_inverted = false; result.witness_index = IS_CONSTANT; } - } else if (witness_index == IS_CONSTANT && other.witness_index != IS_CONSTANT) { + } else if (is_constant() && !other.is_constant()) { if (witness_bool ^ witness_inverted) { result = bool_t(other); } else { @@ -196,14 +186,14 @@ template bool_t bool_t::operator&(const boo template bool_t bool_t::operator|(const bool_t& other) const { - bool_t result(context == nullptr ? other.context : context); + bool_t result(context ? context : other.context); - ASSERT(result.context || (witness_index == IS_CONSTANT && other.witness_index == IS_CONSTANT)); + ASSERT(result.context || (is_constant() && other.is_constant())); result.witness_bool = (witness_bool ^ witness_inverted) | (other.witness_bool ^ other.witness_inverted); bb::fr value = result.witness_bool ? bb::fr::one() : bb::fr::zero(); result.witness_inverted = false; - if ((other.witness_index != IS_CONSTANT) && (witness_index != IS_CONSTANT)) { + if (!is_constant() && !other.is_constant()) { result.witness_index = context->add_variable(value); // result = A + B - AB, where A,B are the "real" values of the variables. But according to whether // witness_inverted flag is true, we need to invert the input. Hence, we look at four cases, and compute the @@ -251,7 +241,7 @@ template bool_t bool_t::operator|(const boo right_coefficient, bb::fr::neg_one(), constant_coefficient }); - } else if (witness_index != IS_CONSTANT && other.witness_index == IS_CONSTANT) { + } else if (!is_constant() && other.is_constant()) { if (other.witness_bool ^ other.witness_inverted) { result.witness_index = IS_CONSTANT; result.witness_bool = true; @@ -259,7 +249,7 @@ template bool_t bool_t::operator|(const boo } else { result = bool_t(*this); } - } else if (witness_index == IS_CONSTANT && other.witness_index != IS_CONSTANT) { + } else if (is_constant() && !other.is_constant()) { if (witness_bool ^ witness_inverted) { result.witness_index = IS_CONSTANT; result.witness_bool = true; @@ -279,13 +269,13 @@ template bool_t bool_t::operator^(const boo { bool_t result(context == nullptr ? other.context : context); - ASSERT(result.context || (witness_index == IS_CONSTANT && other.witness_index == IS_CONSTANT)); + ASSERT(result.context || (is_constant() && other.is_constant())); result.witness_bool = (witness_bool ^ witness_inverted) ^ (other.witness_bool ^ other.witness_inverted); bb::fr value = result.witness_bool ? bb::fr::one() : bb::fr::zero(); result.witness_inverted = false; - if ((other.witness_index != IS_CONSTANT) && (witness_index != IS_CONSTANT)) { + if (!is_constant() && !other.is_constant()) { result.witness_index = context->add_variable(value); // norm a, norm b: a + b - 2ab // inv a, norm b: (1 - a) + b - 2(1 - a)b = 1 - a - b + 2ab @@ -314,14 +304,14 @@ template bool_t bool_t::operator^(const boo right_coefficient, bb::fr::neg_one(), constant_coefficient }); - } else if (witness_index != IS_CONSTANT && other.witness_index == IS_CONSTANT) { + } else if (!is_constant() && other.is_constant()) { // witness ^ 1 = !witness if (other.witness_bool ^ other.witness_inverted) { result = !bool_t(*this); } else { result = bool_t(*this); } - } else if (witness_index == IS_CONSTANT && other.witness_index != IS_CONSTANT) { + } else if (is_constant() && !other.is_constant()) { if (witness_bool ^ witness_inverted) { result = !bool_t(other); } else { @@ -348,27 +338,27 @@ template bool_t bool_t::operator!() const template bool_t bool_t::operator==(const bool_t& other) const { - ASSERT(context || other.context || (witness_index == IS_CONSTANT && other.witness_index == IS_CONSTANT)); - if ((other.witness_index == IS_CONSTANT) && (witness_index == IS_CONSTANT)) { + ASSERT(context || other.context || (is_constant() && other.is_constant())); + if (is_constant() && other.is_constant()) { bool_t result(context == nullptr ? other.context : context); result.witness_bool = (witness_bool ^ witness_inverted) == (other.witness_bool ^ other.witness_inverted); result.witness_index = IS_CONSTANT; result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag())); return result; - } else if ((witness_index != IS_CONSTANT) && (other.witness_index == IS_CONSTANT)) { + } else if (!is_constant() && (other.is_constant())) { if (other.witness_bool ^ other.witness_inverted) { return (*this); } else { return !(*this); } - } else if ((witness_index == IS_CONSTANT) && (other.witness_index != IS_CONSTANT)) { + } else if (is_constant() && !other.is_constant()) { if (witness_bool ^ witness_inverted) { return other; } else { return !(other); } } else { - bool_t result(context == nullptr ? other.context : context); + bool_t result(context ? context : other.context); result.witness_bool = (witness_bool ^ witness_inverted) == (other.witness_bool ^ other.witness_inverted); bb::fr value = result.witness_bool ? bb::fr::one() : bb::fr::zero(); result.witness_index = context->add_variable(value); @@ -458,8 +448,8 @@ bool_t bool_t::conditional_assign(const bool_t& predi } bool same = lhs.witness_index == rhs.witness_index; - bool witness_same = same && lhs.witness_index != IS_CONSTANT && (lhs.witness_inverted == rhs.witness_inverted); - bool const_same = same && (lhs.witness_index == IS_CONSTANT) && (lhs.witness_bool == rhs.witness_bool); + bool witness_same = same && !lhs.is_constant() && (lhs.witness_inverted == rhs.witness_inverted); + bool const_same = same && lhs.is_constant() && (lhs.witness_bool == rhs.witness_bool); if (witness_same || const_same) { return lhs; } From 258662e33d80cdc25b78fade69020a4eef78d8ea Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Wed, 25 Jun 2025 13:53:44 +0000 Subject: [PATCH 02/18] or operator clean-up --- .../stdlib/primitives/bool/bool.cpp | 104 +++++++----------- 1 file changed, 37 insertions(+), 67 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp index b74d688928ef..7e380088e56b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp @@ -110,7 +110,7 @@ template bool_t bool_t::operator&(const boo ASSERT(result.context || (is_constant() && other.is_constant())); if (!is_constant() && !other.is_constant()) { - result.witness_bool = left & right; + result.witness_bool = left && right; bb::fr value = result.witness_bool ? bb::fr::one() : bb::fr::zero(); result.witness_index = context->add_variable(value); result.witness_inverted = false; @@ -139,10 +139,12 @@ template bool_t bool_t::operator&(const boo * + w_b.(i_a.(1 - 2.i_b)) -> q_2 coefficient * + i_a.i_b -> q_c coefficient * + * Note that since the value of the product above is always boolean, we don't need to add an explicit range + * constraint for `result`. **/ - int i_a(witness_inverted); - int i_b(other.witness_inverted); + int i_a(static_cast(witness_inverted)); + int i_b(static_cast(other.witness_inverted)); fr qm(1 - 2 * i_b - 2 * i_a + 4 * i_a * i_b); fr q1(i_b * (1 - 2 * i_a)); @@ -192,74 +194,42 @@ template bool_t bool_t::operator|(const boo result.witness_bool = (witness_bool ^ witness_inverted) | (other.witness_bool ^ other.witness_inverted); bb::fr value = result.witness_bool ? bb::fr::one() : bb::fr::zero(); - result.witness_inverted = false; if (!is_constant() && !other.is_constant()) { result.witness_index = context->add_variable(value); - // result = A + B - AB, where A,B are the "real" values of the variables. But according to whether - // witness_inverted flag is true, we need to invert the input. Hence, we look at four cases, and compute the - // relevent coefficients of the selector q_1,q_2,q_m,q_c in each case - bb::fr multiplicative_coefficient; - bb::fr left_coefficient; - bb::fr right_coefficient; - bb::fr constant_coefficient; - // a inverted: (1-a) + b - (1-a)b = 1-a+ab - // ==> q_1=-1,q_2=0,q_m=1,q_c=1 - if (witness_inverted && !other.witness_inverted) { - multiplicative_coefficient = bb::fr::one(); - left_coefficient = bb::fr::neg_one(); - right_coefficient = bb::fr::zero(); - constant_coefficient = bb::fr::one(); - } - // b inverted: a + (1-b) - a(1-b) = 1-b+ab - // ==> q_1=0,q_2=-1,q_m=1,q_c=1 - else if (!witness_inverted && other.witness_inverted) { - multiplicative_coefficient = bb::fr::one(); - left_coefficient = bb::fr::zero(); - right_coefficient = bb::fr::neg_one(); - constant_coefficient = bb::fr::one(); - } - // Both inverted: (1 - a) + (1 - b) - (1 - a)(1 - b) = 2 - a - b - (1 -a -b +ab) = 1 - ab - // ==> q_m=-1,q_1=0,q_2=0,q_c=1 - else if (witness_inverted && other.witness_inverted) { - multiplicative_coefficient = bb::fr::neg_one(); - left_coefficient = bb::fr::zero(); - right_coefficient = bb::fr::zero(); - constant_coefficient = bb::fr::one(); - } - // No inversions: a + b - ab ==> q_m=-1,q_1=1,q_2=1,q_c=0 - else { - multiplicative_coefficient = bb::fr::neg_one(); - left_coefficient = bb::fr::one(); - right_coefficient = bb::fr::one(); - constant_coefficient = bb::fr::zero(); - } - context->create_poly_gate({ witness_index, - other.witness_index, - result.witness_index, - multiplicative_coefficient, - left_coefficient, - right_coefficient, - bb::fr::neg_one(), - constant_coefficient }); + // Let + // a := lhs = *this; + // b := rhs = other; + // The result is given by + // a + b - a * b = [-(1 - 2*i_a) * (1 - 2*i_b)] * w_a w_b + + // [(1 - 2 * i_a) * (1 - i_b)] * w_a + // [(1 - 2 * i_b) * (1 - i_a)] * w_b + // [i_a + i_b - i_a * i_b] * 1 + const int rhs_inverted = static_cast(other.witness_inverted); + const int lhs_inverted = static_cast(witness_inverted); + + bb::fr q_m{ -(1 - 2 * rhs_inverted) * (1 - 2 * lhs_inverted) }; + bb::fr q_l{ (1 - 2 * lhs_inverted) * (1 - rhs_inverted) }; + bb::fr q_r{ (1 - lhs_inverted) * (1 - 2 * rhs_inverted) }; + bb::fr q_o{ bb::fr::neg_one() }; + bb::fr q_c{ rhs_inverted + lhs_inverted - rhs_inverted * lhs_inverted }; + + // Let r := a | b; + // Constrain + // q_m * w_a * w_b + q_l * w_a + q_r * w_b + q_o * r + q_c = 0 + context->create_poly_gate( + { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c }); } else if (!is_constant() && other.is_constant()) { - if (other.witness_bool ^ other.witness_inverted) { - result.witness_index = IS_CONSTANT; - result.witness_bool = true; - result.witness_inverted = false; - } else { - result = bool_t(*this); - } + ASSERT(other.witness_inverted == false); + + // If we are computing a | b and b is a constant `true`, the result is a constant `true` that does not + // depend on `a`. + result = other.witness_bool ? other : *this; + } else if (is_constant() && !other.is_constant()) { - if (witness_bool ^ witness_inverted) { - result.witness_index = IS_CONSTANT; - result.witness_bool = true; - result.witness_inverted = false; - } else { - result = bool_t(other); - } - } else { - result.witness_inverted = false; - result.witness_index = IS_CONSTANT; + // If we are computing a | b and `a` is a constant `true`, the result is a constant `true` that does not + // depend on `b`. + ASSERT(witness_inverted == false); + result = witness_bool ? *this : other; } result.tag = OriginTag(tag, other.tag); return result; From b5d7d54034701a7ead1c65a0ecdf5bbb54fdcf22 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Wed, 25 Jun 2025 14:33:05 +0000 Subject: [PATCH 03/18] xor clean-up --- .../stdlib/primitives/bool/bool.cpp | 71 ++++++++----------- 1 file changed, 30 insertions(+), 41 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp index 7e380088e56b..be8c12c5b868 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp @@ -178,7 +178,7 @@ template bool_t bool_t::operator&(const boo result.witness_index = IS_CONSTANT; } } else { - result.witness_bool = left & right; + result.witness_bool = left && right; result.witness_index = IS_CONSTANT; result.witness_inverted = false; } @@ -247,49 +247,38 @@ template bool_t bool_t::operator^(const boo if (!is_constant() && !other.is_constant()) { result.witness_index = context->add_variable(value); - // norm a, norm b: a + b - 2ab - // inv a, norm b: (1 - a) + b - 2(1 - a)b = 1 - a - b + 2ab - // norm a, inv b: a + (1 - b) - 2(a)(1 - b) = 1 - a - b + 2ab - // inv a, inv b: (1 - a) + (1 - b) - 2(1 - a)(1 - b) = a + b - 2ab - bb::fr multiplicative_coefficient; - bb::fr left_coefficient; - bb::fr right_coefficient; - bb::fr constant_coefficient; - if ((witness_inverted && other.witness_inverted) || (!witness_inverted && !other.witness_inverted)) { - multiplicative_coefficient = (bb::fr::neg_one() + bb::fr::neg_one()); - left_coefficient = bb::fr::one(); - right_coefficient = bb::fr::one(); - constant_coefficient = bb::fr::zero(); - } else { - multiplicative_coefficient = bb::fr::one() + bb::fr::one(); - left_coefficient = bb::fr::neg_one(); - right_coefficient = bb::fr::neg_one(); - constant_coefficient = bb::fr::one(); - } - context->create_poly_gate({ witness_index, - other.witness_index, - result.witness_index, - multiplicative_coefficient, - left_coefficient, - right_coefficient, - bb::fr::neg_one(), - constant_coefficient }); + // Let + // a := lhs = *this; + // b := rhs = other; + // The result is given by + // a + b - 2 * a * b = [-2 *(1 - 2*i_a) * (1 - 2*i_b)] * w_a w_b + + // [(1 - 2 * i_a) * (1 - 2 * i_b)] * w_a + // [(1 - 2 * i_b) * (1 - 2 * i_a)] * w_b + // [i_a + i_b - 2 * i_a * i_b] * 1] + const int rhs_inverted = static_cast(other.witness_inverted); + const int lhs_inverted = static_cast(witness_inverted); + // Compute the value that's being used in several selectors + const int aux_prod = (1 - 2 * rhs_inverted) * (1 - 2 * lhs_inverted); + + bb::fr q_m{ -2 * aux_prod }; + bb::fr q_l{ aux_prod }; + bb::fr q_r{ aux_prod }; + bb::fr q_o{ bb::fr::neg_one() }; + bb::fr q_c{ lhs_inverted + rhs_inverted - 2 * rhs_inverted * lhs_inverted }; + + // Let r := a ^ b; + // Constrain + // q_m * w_a * w_b + q_l * w_a + q_r * w_b + q_o * r + q_c = 0 + context->create_poly_gate( + { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c }); } else if (!is_constant() && other.is_constant()) { // witness ^ 1 = !witness - if (other.witness_bool ^ other.witness_inverted) { - result = !bool_t(*this); - } else { - result = bool_t(*this); - } + ASSERT(other.witness_inverted == false); + result = other.witness_bool ? !*this : *this; + } else if (is_constant() && !other.is_constant()) { - if (witness_bool ^ witness_inverted) { - result = !bool_t(other); - } else { - result = bool_t(other); - } - } else { - result.witness_inverted = false; - result.witness_index = IS_CONSTANT; + ASSERT(witness_inverted == false); + result = witness_bool ? !other : other; } result.tag = OriginTag(tag, other.tag); return result; From 66c38ebfa99821f95bcb685feb2804484f2b2318 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Wed, 25 Jun 2025 15:40:40 +0000 Subject: [PATCH 04/18] == operator clean-up --- .../stdlib/primitives/bool/bool.cpp | 92 +++++++++---------- 1 file changed, 42 insertions(+), 50 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp index be8c12c5b868..e55c76616a4e 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp @@ -298,58 +298,50 @@ template bool_t bool_t::operator!() const template bool_t bool_t::operator==(const bool_t& other) const { ASSERT(context || other.context || (is_constant() && other.is_constant())); - if (is_constant() && other.is_constant()) { - bool_t result(context == nullptr ? other.context : context); - result.witness_bool = (witness_bool ^ witness_inverted) == (other.witness_bool ^ other.witness_inverted); - result.witness_index = IS_CONSTANT; - result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag())); - return result; + bool_t result(context ? context : other.context); + + result.witness_bool = (witness_bool ^ witness_inverted) == (other.witness_bool ^ other.witness_inverted); + if (!is_constant() && !other.is_constant()) { + const bb::fr value = result.witness_bool ? bb::fr::one() : bb::fr::zero(); + + result.witness_index = context->add_variable(value); + + // Let + // a := lhs = *this; + // b := rhs = other; + // The result is given by + // 1 - a - b + 2 a * b = [2 *(1 - 2*i_a) * (1 - 2*i_b)] * w_a w_b + + // [-(1 - 2 * i_a) * (1 - 2 * i_b)] * w_a + // [-(1 - 2 * i_b) * (1 - 2 * i_a)] * w_b + // [1 - i_a - i_b + 2 * i_a * i_b] * 1 + const int rhs_inverted = static_cast(other.witness_inverted); + const int lhs_inverted = static_cast(witness_inverted); + // Compute the value that's being used in several selectors + const int aux_prod = (1 - 2 * rhs_inverted) * (1 - 2 * lhs_inverted); + bb::fr q_m{ 2 * aux_prod }; + bb::fr q_l{ -aux_prod }; + bb::fr q_r{ -aux_prod }; + bb::fr q_o{ bb::fr::neg_one() }; + bb::fr q_c{ 1 - lhs_inverted - rhs_inverted + 2 * rhs_inverted * lhs_inverted }; + + context->create_poly_gate( + { witness_index, other.witness_index, result.witness_index, q_m, q_r, q_l, q_o, q_c }); + } else if (!is_constant() && (other.is_constant())) { - if (other.witness_bool ^ other.witness_inverted) { - return (*this); - } else { - return !(*this); - } + // Compare *this with a constant other. If other == true, then we're checking *this == true. In this case we + // propagate *this without adding extra constraints, otherwise (if other = false), we propagate !*this. + ASSERT(other.witness_inverted == false); + result = other.witness_bool ? *this : !(*this); + return result; } else if (is_constant() && !other.is_constant()) { - if (witness_bool ^ witness_inverted) { - return other; - } else { - return !(other); - } - } else { - bool_t result(context ? context : other.context); - result.witness_bool = (witness_bool ^ witness_inverted) == (other.witness_bool ^ other.witness_inverted); - bb::fr value = result.witness_bool ? bb::fr::one() : bb::fr::zero(); - result.witness_index = context->add_variable(value); - // norm a, norm b or both inv: 1 - a - b + 2ab - // inv a or inv b = a + b - 2ab - bb::fr multiplicative_coefficient; - bb::fr left_coefficient; - bb::fr right_coefficient; - bb::fr constant_coefficient; - if ((witness_inverted && other.witness_inverted) || (!witness_inverted && !other.witness_inverted)) { - multiplicative_coefficient = bb::fr::one() + bb::fr::one(); - left_coefficient = bb::fr::neg_one(); - right_coefficient = bb::fr::neg_one(); - constant_coefficient = bb::fr::one(); - } else { - multiplicative_coefficient = (bb::fr::neg_one() + bb::fr::neg_one()); - left_coefficient = bb::fr::one(); - right_coefficient = bb::fr::one(); - constant_coefficient = bb::fr::zero(); - } - context->create_poly_gate({ witness_index, - other.witness_index, - result.witness_index, - multiplicative_coefficient, - left_coefficient, - right_coefficient, - bb::fr::neg_one(), - constant_coefficient }); - - result.tag = OriginTag(tag, other.tag); + // Completely analogous to the previous case. + ASSERT(witness_inverted == false); + result = witness_bool ? other : !other; return result; } + + result.tag = OriginTag(tag, other.tag); + return result; } template bool_t bool_t::operator!=(const bool_t& other) const @@ -427,8 +419,8 @@ template void bool_t::must_imply(const bool_t& other /** * Process many implications all at once, for readablity, and as an optimization. - * @param conds - each pair is a boolean condition that we want to constrain to be "implied", and an error message if it - * is not implied. + * @param conds - each pair is a boolean condition that we want to constrain to be "implied", and an error message + * if it is not implied. * * Why this works: * (P => Q) && (P => R) From 33cf9cfe10de2d02bef64a5e98ded24ceef549a3 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Thu, 26 Jun 2025 09:31:08 +0000 Subject: [PATCH 05/18] docs for normalize --- .../stdlib/primitives/bool/bool.cpp | 51 ++++++++++--------- .../stdlib/primitives/bool/bool.test.cpp | 3 ++ 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp index e55c76616a4e..a23802be81a0 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp @@ -288,10 +288,13 @@ template bool_t bool_t::operator!() const { bool_t result(*this); if (result.is_constant()) { + ASSERT(!witness_inverted); + // Negate the value of a constant bool_t element. result.witness_bool = !result.witness_bool; - return result; + } else { + // Negate the `inverted` flag of a witnees bool_t element. + result.witness_inverted = !result.witness_inverted; } - result.witness_inverted = !result.witness_inverted; return result; } @@ -311,8 +314,8 @@ template bool_t bool_t::operator==(const bo // b := rhs = other; // The result is given by // 1 - a - b + 2 a * b = [2 *(1 - 2*i_a) * (1 - 2*i_b)] * w_a w_b + - // [-(1 - 2 * i_a) * (1 - 2 * i_b)] * w_a - // [-(1 - 2 * i_b) * (1 - 2 * i_a)] * w_b + // [-(1 - 2 * i_a) * (1 - 2 * i_b)] * w_a + + // [-(1 - 2 * i_b) * (1 - 2 * i_a)] * w_b + // [1 - i_a - i_b + 2 * i_a * i_b] * 1 const int rhs_inverted = static_cast(other.witness_inverted); const int lhs_inverted = static_cast(witness_inverted); @@ -332,12 +335,10 @@ template bool_t bool_t::operator==(const bo // propagate *this without adding extra constraints, otherwise (if other = false), we propagate !*this. ASSERT(other.witness_inverted == false); result = other.witness_bool ? *this : !(*this); - return result; } else if (is_constant() && !other.is_constant()) { // Completely analogous to the previous case. ASSERT(witness_inverted == false); result = witness_bool ? other : !other; - return result; } result.tag = OriginTag(tag, other.tag); @@ -368,15 +369,15 @@ template void bool_t::assert_equal(const bool_t& rhs ASSERT(lhs.get_value() == rhs.get_value()); } else if (lhs.is_constant()) { // if rhs is inverted, flip the value of the lhs constant - bool lhs_value = rhs.witness_inverted ? !lhs.get_value() : lhs.get_value(); + const bool lhs_value = rhs.witness_inverted ? !lhs.witness_bool : lhs.witness_bool; ctx->assert_equal_constant(rhs.witness_index, lhs_value, msg); } else if (rhs.is_constant()) { // if lhs is inverted, flip the value of the rhs constant - bool rhs_value = lhs.witness_inverted ? !rhs.get_value() : rhs.get_value(); + const bool rhs_value = lhs.witness_inverted ? !rhs.witness_bool : rhs.witness_bool; ctx->assert_equal_constant(lhs.witness_index, rhs_value, msg); } else { - auto left = lhs; - auto right = rhs; + bool_t left = lhs; + bool_t right = rhs; // we need to normalize iff lhs or rhs has an inverted witness (but not both) if (lhs.witness_inverted ^ rhs.witness_inverted) { left = left.normalize(); @@ -409,12 +410,13 @@ bool_t bool_t::conditional_assign(const bool_t& predi template bool_t bool_t::implies(const bool_t& other) const { + // Thanks to negation operator being free, this computation requires at most 1 gate. return (!(*this) || other); // P => Q is equiv. to !P || Q (not(P) or Q). } template void bool_t::must_imply(const bool_t& other, std::string const& msg) const { - (this->implies(other)).assert_equal(true, msg); + implies(other).assert_equal(true, msg); } /** @@ -488,31 +490,34 @@ template bool_t bool_t::implies_both_ways(c return (!(*this) ^ other); // P <=> Q is equiv. to !(P ^ Q) (not(P xor Q)). } +/** + * @brief A bool_t element is normalized if witness_inverted == false. For a given *this, output its normalized version. + * + */ template bool_t bool_t::normalize() const { if (is_constant()) { - ASSERT(!this->witness_inverted); + ASSERT(!witness_inverted); return *this; } + // Let a := *this, need to constrain a = a_norm + // [1 - 2 i_a] w_a + [-1] w_a_norm + [i_a] = 0 + // ^ ^ ^ + // q_l q_o q_c + const bool value = witness_bool ^ witness_inverted; - bb::fr value = witness_bool ^ witness_inverted ? bb::fr::one() : bb::fr::zero(); - - uint32_t new_witness = context->add_variable(value); - uint32_t new_value = witness_bool ^ witness_inverted; - - bb::fr q_l; - bb::fr q_c; - - q_l = witness_inverted ? bb::fr::neg_one() : bb::fr::one(); - q_c = witness_inverted ? bb::fr::one() : bb::fr::zero(); + uint32_t new_witness = context->add_variable(bb::fr{ static_cast(value) }); + const int inverted = static_cast(witness_inverted); + bb::fr q_l{ 1 - 2 * inverted }; + bb::fr q_c{ inverted }; bb::fr q_o = bb::fr::neg_one(); bb::fr q_m = bb::fr::zero(); bb::fr q_r = bb::fr::zero(); context->create_poly_gate({ witness_index, witness_index, new_witness, q_m, q_l, q_r, q_o, q_c }); witness_index = new_witness; - witness_bool = new_value; + witness_bool = value; witness_inverted = false; return *this; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp index 0c56a59f11af..21d1a4ff3041 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp @@ -370,7 +370,10 @@ TYPED_TEST(BoolTest, ImpliesBothWays) a.set_origin_tag(submitted_value_origin_tag); b.set_origin_tag(challenge_origin_tag); } + info("num before ", builder.get_estimated_num_finalized_gates()); bool_ct c = a.implies_both_ways(b); + info("num after ", builder.get_estimated_num_finalized_gates()); + EXPECT_EQ(c.get_value(), !(a.get_value() ^ b.get_value())); if (!(lhs_constant || rhs_constant)) { From 96fea655d757751cc523dc3226bd1f7f10e0823a Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Fri, 27 Jun 2025 11:54:45 +0000 Subject: [PATCH 06/18] general docs --- .../stdlib/primitives/bool/bool.cpp | 13 +++---- .../stdlib/primitives/bool/bool.hpp | 39 ++++++++++++++++++- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp index a23802be81a0..fcd08176f050 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp @@ -14,9 +14,7 @@ namespace bb::stdlib { template bool_t::bool_t(const bool value) - : context(nullptr) - , witness_bool(value) - , witness_index(IS_CONSTANT) + : witness_bool(value) {} template @@ -462,7 +460,7 @@ void bool_t::must_imply(const std::vectorget_value(); + const bool this_val = get_value(); bool error_found = false; std::string error_msg; @@ -472,7 +470,7 @@ void bool_t::must_imply(const std::vector::must_imply(const std::vectorimplies(acc)).assert_equal(true, format("multi implication fail: ", error_msg)); + implies(acc).assert_equal(true, format("multi implication fail: ", error_msg)); } // A "double-implication" (<=>), @@ -491,7 +489,8 @@ template bool_t bool_t::implies_both_ways(c } /** - * @brief A bool_t element is normalized if witness_inverted == false. For a given *this, output its normalized version. + * @brief A bool_t element is **normalized** if `witness_inverted == false`. For a given `*this`, output its normalized + * version. * */ template bool_t bool_t::normalize() const diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.hpp index 6dff662a2673..b6483d5ba5ef 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.hpp @@ -10,7 +10,44 @@ #include "barretenberg/transcript/origin_tag.hpp" namespace bb::stdlib { - +/** + * @brief Implements boolean logic in-circuit. + * + * @details + * ## Representing bools in-circuit + * + * To avoid constraining negation operations, we represent an in-circuit boolean \f$ a \f$ by a witness value \f$ w_a + * \f$ and an `witness_inverted` flag \f$ i_a \f$. The value of \f$ a \f$ is defined via the equation: + * + * \f{align}{ w_a + i_a - 2 \cdot i_a \cdot w_a \f} + * + * When a new bool_t element \f$ a \f$ is created, its `witness_inverted` flag is set to `false` and + * its `witness_value` is constrained to be \f$ 0 \f$ or \f$ 1\f$. More precisely, if \f$ a \f$ is a witness, then we + * add a boolean constraint ensuring \f$ w_a^2 = w_a \f$, if \f$ a \f$ is a constant bool_t element, then it's checked + * by an **ASSERT**. + * + * To negate \f$ a \f$ we simply flip the flag. Other basic operations are deduced from their algebraic representations + * and the result is constrained to satisfy corresponding algebraic equation. + * + * ## Detailed example of an operation (logical OR) + * + * For example, to produce \f$ a || b \f$ in circuit, we compute + * \f{align}{ + * a + b - a \cdot b = ( w_a \cdot (1- 2 i_a) + i_a) + ( w_b \cdot (1 - 2 i_b) + i_b) - + * ( w_a \cdot (1-2 i_a) + i_a) ( w_b \cdot (1 - 2 i_b) + i_b) + * \f} + * Thus we can use a `poly` gate to constrain the result of a || b as follows: + * + * \f{align}{ q_m \cdot w_a \cdot w_b + q_l \cdot w_a + q_r \cdot w_b + q_o \cdot (a || b) + q_c = 0\f} + * where + * \f{eqnarray*} q_m &=& -(1 - 2*i_a) * (1 - 2*i_b) \\ + * q_l &=& (1 - 2 * i_a) * (1 - i_b) \\ + * q_r &=& (1 - 2 * i_b) * (1 - i_a) \\ + * q_o &=& -1 \\ + * q_c &=& i_a + i_b - i_a * i_b \f} + * As \f$ w_a \f$ and \f$ w_b \f$ are constrained to be boolean, \f$ i_a \f$, \f$ i_b\f$ are boolean flags, we see that + * \f$ (a || b)^2 = (a || b)\f$ (as a field element). + */ template class bool_t { public: bool_t(const bool value = false); From a301d1a75608d242c1ad01821ed47ed1882960d0 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Mon, 30 Jun 2025 10:04:25 +0000 Subject: [PATCH 07/18] improving the tests --- .../stdlib/primitives/bool/bool.cpp | 59 +++---- .../stdlib/primitives/bool/bool.hpp | 10 +- .../stdlib/primitives/bool/bool.test.cpp | 149 +++++++++++++++++- 3 files changed, 173 insertions(+), 45 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp index fcd08176f050..eec68cfad9ce 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp @@ -26,7 +26,8 @@ template bool_t::bool_t(const witness_t& value) : context(value.context) { - ASSERT((value.witness == bb::fr::zero()) || (value.witness == bb::fr::one())); + ASSERT((value.witness == bb::fr::zero()) || (value.witness == bb::fr::one()), + "bool_t: witness value is not 0 or 1"); witness_index = value.witness_index; context->create_bool_gate(witness_index); witness_bool = (value.witness == bb::fr::one()); @@ -105,13 +106,12 @@ template bool_t bool_t::operator&(const boo bool_t result(context ? context : other.context); bool left = witness_inverted ^ witness_bool; bool right = other.witness_inverted ^ other.witness_bool; + result.witness_bool = left && right; ASSERT(result.context || (is_constant() && other.is_constant())); if (!is_constant() && !other.is_constant()) { - result.witness_bool = left && right; bb::fr value = result.witness_bool ? bb::fr::one() : bb::fr::zero(); result.witness_index = context->add_variable(value); - result.witness_inverted = false; /** * A bool can be represented by a witness value `w` and an 'inverted' flag `i` @@ -144,42 +144,27 @@ template bool_t bool_t::operator&(const boo int i_a(static_cast(witness_inverted)); int i_b(static_cast(other.witness_inverted)); - fr qm(1 - 2 * i_b - 2 * i_a + 4 * i_a * i_b); - fr q1(i_b * (1 - 2 * i_a)); - fr q2(i_a * (1 - 2 * i_b)); - fr q3(-1); - fr qc(i_a * i_b); - context->create_poly_gate({ - witness_index, - other.witness_index, - result.witness_index, - qm, - q1, - q2, - q3, - qc, - }); + fr q_m{ 1 - 2 * i_b - 2 * i_a + 4 * i_a * i_b }; + fr q_l{ i_b * (1 - 2 * i_a) }; + fr q_r{ i_a * (1 - 2 * i_b) }; + fr q_o{ -1 }; + fr q_c{ i_a * i_b }; + + context->create_poly_gate( + { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c }); } else if (!is_constant() && other.is_constant()) { - if (other.witness_bool ^ other.witness_inverted) { - result = bool_t(*this); - } else { - result.witness_bool = false; - result.witness_inverted = false; - result.witness_index = IS_CONSTANT; - } + ASSERT(!other.witness_inverted); + // If rhs is a constant true, the output is determined by the lhs. Otherwise the output is a constant + // `false`. + result = other.witness_bool ? *this : other; + } else if (is_constant() && !other.is_constant()) { - if (witness_bool ^ witness_inverted) { - result = bool_t(other); - } else { - result.witness_bool = false; - result.witness_inverted = false; - result.witness_index = IS_CONSTANT; - } - } else { - result.witness_bool = left && right; - result.witness_index = IS_CONSTANT; - result.witness_inverted = false; + ASSERT(!witness_inverted); + // If lhs is a constant true, the output is determined by the rhs. Otherwise the output is a constant + // `false`. + result = witness_bool ? other : *this; } + result.tag = OriginTag(tag, other.tag); return result; } @@ -459,7 +444,7 @@ void bool_t::must_imply(const std::vector @@ -19,11 +18,150 @@ namespace { auto& engine = numeric::get_debug_randomness(); } STANDARD_TESTING_TAGS -template class BoolTest : public ::testing::Test {}; +template class BoolTest : public ::testing::Test { + public: + using Builder = Builder_; + using witness_ct = stdlib::witness_t; + using bool_ct = stdlib::bool_t; + + // These tree boolean flags cover all possible combinations for a valid input. + struct BoolInput { + bool is_const; // witness_index = IS_CONSTANT + bool value; // w_a + bool is_inverted; // i_a + }; + + // A helper to produce all possible inputs for a given operand. + std::array all_inputs = []() { + std::array inputs{}; + size_t idx = 0; + for (bool is_const : { false, true }) { + for (bool value : { false, true }) { + for (bool is_inverted : { false, true }) { + inputs[idx++] = BoolInput{ is_const, value, is_inverted }; + } + } + } + return inputs; + }(); + // A helper to create a bool_t element from the given flags + static bool_ct create_bool_ct(const BoolInput& in, Builder* builder) + { + bool_ct b = in.is_const ? bool_ct(in.value) : witness_ct(builder, in.value); + return in.is_inverted ? !b : b; + }; + + void test_binary_op(std::string const& op_name, + const std::function& op, + const std::function& expected_op) + { + Builder builder; + + for (auto& lhs : all_inputs) { + for (auto& rhs : all_inputs) { + bool_ct a = create_bool_ct(lhs, &builder); + bool_ct b = create_bool_ct(rhs, &builder); + + size_t num_gates_start = builder.get_estimated_num_finalized_gates(); + + bool_ct c = op(a, b); + + bool expected = expected_op(lhs.value ^ lhs.is_inverted, rhs.value ^ rhs.is_inverted); + + EXPECT_EQ(c.get_value(), expected) + << "Failed on " << op_name << " with inputs: lhs = {const=" << lhs.is_const << ", val=" << lhs.value + << ", inv=" << lhs.is_inverted << "}, rhs = {const=" << rhs.is_const << ", val=" << rhs.value + << ", inv=" << rhs.is_inverted << "}"; + + if (a.is_constant() && b.is_constant()) { + EXPECT_TRUE(c.is_constant()); + } + + if (!a.is_constant() && !b.is_constant()) { + EXPECT_TRUE(!c.is_constant()); + } + + size_t diff = builder.get_estimated_num_finalized_gates() - num_gates_start; + EXPECT_EQ(diff, static_cast(!a.is_constant() && !b.is_constant())); + } + } + + EXPECT_TRUE(CircuitChecker::check(builder)); + }; + + void test_construct_from_const_bool() + { + Builder builder = Builder(); + size_t num_gates_start = builder.get_estimated_num_finalized_gates(); + bool_ct a_true(true); + bool_ct a_false(false); + EXPECT_TRUE(a_true.get_value()); + EXPECT_FALSE(a_false.get_value()); + EXPECT_TRUE(a_true.is_constant() && a_false.is_constant()); + EXPECT_TRUE(!a_true.witness_inverted && !a_false.witness_inverted); + // No gates have been added + EXPECT_TRUE(num_gates_start == builder.get_estimated_num_finalized_gates()); + } + + void test_construct_from_witness() + { + Builder builder = Builder(); + size_t num_gates_start = builder.get_estimated_num_finalized_gates(); + + bool_ct a_true = witness_ct(&builder, 1); + bool_ct a_false = witness_ct(&builder, 0); + EXPECT_TRUE(a_true.get_value()); + EXPECT_FALSE(a_false.get_value()); + EXPECT_TRUE(!a_true.is_constant() && !a_false.is_constant()); + EXPECT_TRUE(!a_true.witness_inverted && !a_false.witness_inverted); + // Each witness bool must be constrained => expect 2 gates being added + EXPECT_TRUE(builder.get_estimated_num_finalized_gates() - num_gates_start == 2); + EXPECT_TRUE(CircuitChecker::check(builder)); + +#ifndef NDEBUG + // Test failure + bool_ct a_incorrect; + uint256_t random_value(engine.get_random_uint256()); + + if (random_value * random_value - random_value != 0) { + EXPECT_DEATH(a_incorrect = witness_ct(&builder, random_value), "witness value is not 0 or 1"); + }; +#endif + } + void test_AND() + { + test_binary_op( + "AND", [](const bool_ct& a, const bool_ct& b) { return a & b; }, [](bool a, bool b) { return a && b; }); + } + + void test_xor() + { + test_binary_op( + "XOR", [](const bool_ct& a, const bool_ct& b) { return a ^ b; }, [](bool a, bool b) { return a ^ b; }); + } +}; using CircuitTypes = ::testing::Types; TYPED_TEST_SUITE(BoolTest, CircuitTypes); +TYPED_TEST(BoolTest, ConstructFromConstBool) +{ + TestFixture::test_construct_from_const_bool(); +} + +TYPED_TEST(BoolTest, ConstructFromWitness) +{ + TestFixture::test_construct_from_witness(); +} +TYPED_TEST(BoolTest, XOR) +{ + TestFixture::test_xor(); +} + +TYPED_TEST(BoolTest, AND) +{ + TestFixture::test_AND(); +} TYPED_TEST(BoolTest, TestBasicOperations) { @@ -160,7 +298,7 @@ TYPED_TEST(BoolTest, XorTwinConstants) bool result = CircuitChecker::check(builder); EXPECT_EQ(result, true); } - +// Test and for non-normalized bools TYPED_TEST(BoolTest, LogicalAnd) { STDLIB_TYPE_ALIASES @@ -230,7 +368,7 @@ TYPED_TEST(BoolTest, AndConstants) EXPECT_EQ(result, true); } -TYPED_TEST(BoolTest, or) +TYPED_TEST(BoolTest, Or) { STDLIB_TYPE_ALIASES auto builder = Builder(); @@ -370,10 +508,7 @@ TYPED_TEST(BoolTest, ImpliesBothWays) a.set_origin_tag(submitted_value_origin_tag); b.set_origin_tag(challenge_origin_tag); } - info("num before ", builder.get_estimated_num_finalized_gates()); bool_ct c = a.implies_both_ways(b); - info("num after ", builder.get_estimated_num_finalized_gates()); - EXPECT_EQ(c.get_value(), !(a.get_value() ^ b.get_value())); if (!(lhs_constant || rhs_constant)) { From 89b66479e251ffe525fbc5f368ad246cf66acb26 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Mon, 30 Jun 2025 11:22:18 +0000 Subject: [PATCH 08/18] keep cleaning up the tests --- .../stdlib/primitives/bool/bool.test.cpp | 215 +++++------------- 1 file changed, 53 insertions(+), 162 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp index ce86e7289c0a..7e254ce542c6 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp @@ -124,7 +124,8 @@ template class BoolTest : public ::testing::Test { uint256_t random_value(engine.get_random_uint256()); if (random_value * random_value - random_value != 0) { - EXPECT_DEATH(a_incorrect = witness_ct(&builder, random_value), "witness value is not 0 or 1"); + EXPECT_DEATH(a_incorrect = witness_ct(&builder, random_value), + "((other.witness == bb::fr::one()) || (other.witness == bb::fr::zero()))"); }; #endif } @@ -139,6 +140,34 @@ template class BoolTest : public ::testing::Test { test_binary_op( "XOR", [](const bool_ct& a, const bool_ct& b) { return a ^ b; }, [](bool a, bool b) { return a ^ b; }); } + + void test_OR() + { + test_binary_op( + "OR", [](const bool_ct& a, const bool_ct& b) { return a || b; }, [](bool a, bool b) { return a || b; }); + } + + void test_EQ() + { + test_binary_op( + "==", [](const bool_ct& a, const bool_ct& b) { return a == b; }, [](bool a, bool b) { return a == b; }); + } + + void test_implies() + { + test_binary_op( + "=>", + [](const bool_ct& a, const bool_ct& b) { return a.implies(b); }, + [](bool a, bool b) { return !a || b; }); + } + + void test_implies_both_ways() + { + test_binary_op( + "<=>", + [](const bool_ct& a, const bool_ct& b) { return a.implies_both_ways(b); }, + [](bool a, bool b) { return !a ^ b; }); + } }; using CircuitTypes = ::testing::Types; @@ -162,7 +191,27 @@ TYPED_TEST(BoolTest, AND) { TestFixture::test_AND(); } -TYPED_TEST(BoolTest, TestBasicOperations) + +TYPED_TEST(BoolTest, OR) +{ + TestFixture::test_OR(); +} + +TYPED_TEST(BoolTest, EQ) +{ + TestFixture::test_EQ(); +} + +TYPED_TEST(BoolTest, Implies) +{ + TestFixture::test_implies(); +} + +TYPED_TEST(BoolTest, ImpliesBothWays) +{ + TestFixture::test_implies_both_ways(); +} +TYPED_TEST(BoolTest, TestBasicOperationsTags) { STDLIB_TYPE_ALIASES @@ -223,54 +272,6 @@ TYPED_TEST(BoolTest, TestBasicOperations) EXPECT_EQ(gates_after - gates_before, 6UL); } -TYPED_TEST(BoolTest, Xor) -{ - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - for (size_t j = 0; j < 4; ++j) { - bool lhs_constant = (bool)(j % 2); - bool rhs_constant = (bool)(j > 1 ? true : false); - - for (size_t i = 0; i < 4; ++i) { - bool a_val = (bool)(i % 2); - bool b_val = (bool)(i > 1 ? true : false); - bool_ct a = lhs_constant ? bool_ct(a_val) : (witness_ct(&builder, a_val)); - bool_ct b = rhs_constant ? bool_ct(b_val) : (witness_ct(&builder, b_val)); - bool_ct c = a ^ b; - EXPECT_EQ(c.get_value(), a.get_value() ^ b.get_value()); - } - } - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); -} - -TYPED_TEST(BoolTest, XorConstants) -{ - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - for (size_t i = 0; i < 32; ++i) { - bool_ct a = witness_ct(&builder, (bool)(i % 2)); - bool_ct b = witness_ct(&builder, (bool)(i % 3 == 1)); - a ^ b; - } - for (size_t i = 0; i < 32; ++i) { - if (i % 2 == 0) { - bool_ct a = witness_ct(&builder, (bool)(i % 2)); - bool_ct b(&builder, (bool)(i % 3 == 1)); - a ^ b; - } else { - bool_ct a(&builder, (bool)(i % 2)); - bool_ct b = witness_ct(&builder, (bool)(i % 3 == 1)); - a ^ b; - } - } - - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); -} - TYPED_TEST(BoolTest, XorTwinConstants) { STDLIB_TYPE_ALIASES @@ -298,116 +299,6 @@ TYPED_TEST(BoolTest, XorTwinConstants) bool result = CircuitChecker::check(builder); EXPECT_EQ(result, true); } -// Test and for non-normalized bools -TYPED_TEST(BoolTest, LogicalAnd) -{ - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - bool_ct a = witness_ct(&builder, 1); - bool_ct b = witness_ct(&builder, 1); - - a.set_origin_tag(submitted_value_origin_tag); - b.set_origin_tag(challenge_origin_tag); - - auto c = (!a) && (!b); - - // Tags are merged on logical AND - EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag); - - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); -} - -TYPED_TEST(BoolTest, And) -{ - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - for (size_t i = 0; i < 32; ++i) { - bool_ct a = witness_ct(&builder, (bool)(i % 1)); - bool_ct b = witness_ct(&builder, (bool)(i % 2 == 1)); - // clang-format off - a& b; - // clang-format on - } - - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); -} - -TYPED_TEST(BoolTest, AndConstants) -{ - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - for (size_t i = 0; i < 32; ++i) { - bool_ct a = witness_ct(&builder, (bool)(i % 2)); - bool_ct b = witness_ct(&builder, (bool)(i % 3 == 1)); - // clang-format off - a& b; - // clang-format on - } - for (size_t i = 0; i < 32; ++i) { - if (i % 2 == 0) { - bool_ct a = witness_ct(&builder, (bool)(i % 2)); - bool_ct b(&builder, (bool)(i % 3 == 1)); - // clang-format off - a& b; - // clang-format on - } else { - bool_ct a(&builder, (bool)(i % 2)); - bool_ct b = witness_ct(&builder, (bool)(i % 3 == 1)); - // clang-format off - a& b; - // clang-format on - } - } - - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); -} - -TYPED_TEST(BoolTest, Or) -{ - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - for (size_t i = 0; i < 32; ++i) { - bool_ct a = witness_ct(&builder, (bool)(i % 2)); - bool_ct b = witness_ct(&builder, (bool)(i % 3 == 1)); - a | b; - } - - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); -} - -TYPED_TEST(BoolTest, OrConstants) -{ - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - for (size_t i = 0; i < 32; ++i) { - bool_ct a = witness_ct(&builder, (bool)(i % 2)); - bool_ct b = witness_ct(&builder, (bool)(i % 3 == 1)); - a | b; - } - for (size_t i = 0; i < 32; ++i) { - if (i % 2 == 0) { - bool_ct a = witness_ct(&builder, (bool)(i % 2)); - bool_ct b(&builder, (bool)(i % 3 == 1)); - a | b; - } else { - bool_ct a(&builder, (bool)(i % 2)); - bool_ct b = witness_ct(&builder, (bool)(i % 3 == 1)); - a | b; - } - } - - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); -} TYPED_TEST(BoolTest, Eq) { @@ -459,7 +350,7 @@ TYPED_TEST(BoolTest, Eq) EXPECT_EQ(result, true); } -TYPED_TEST(BoolTest, Implies) +TYPED_TEST(BoolTest, ImpliesTags) { STDLIB_TYPE_ALIASES auto builder = Builder(); @@ -490,7 +381,7 @@ TYPED_TEST(BoolTest, Implies) EXPECT_EQ(result, true); } -TYPED_TEST(BoolTest, ImpliesBothWays) +TYPED_TEST(BoolTest, ImpliesBothWaysTags) { STDLIB_TYPE_ALIASES auto builder = Builder(); From 7731a921528165f028ebd4a704f75f27fe97d598 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Mon, 30 Jun 2025 16:03:07 +0000 Subject: [PATCH 09/18] tests tests --- .../stdlib/primitives/bool/bool.cpp | 4 + .../stdlib/primitives/bool/bool.test.cpp | 193 ++++++++---------- 2 files changed, 85 insertions(+), 112 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp index eec68cfad9ce..c5eec3488e27 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp @@ -484,6 +484,10 @@ template bool_t bool_t::normalize() const ASSERT(!witness_inverted); return *this; } + + if (!witness_inverted) { + return *this; + } // Let a := *this, need to constrain a = a_norm // [1 - 2 i_a] w_a + [-1] w_a_norm + [i_a] = 0 // ^ ^ ^ diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp index 7e254ce542c6..1dfc58f85924 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp @@ -64,6 +64,10 @@ template class BoolTest : public ::testing::Test { size_t num_gates_start = builder.get_estimated_num_finalized_gates(); + if (!a.is_constant() && !b.is_constant()) { + a.set_origin_tag(submitted_value_origin_tag); + b.set_origin_tag(challenge_origin_tag); + } bool_ct c = op(a, b); bool expected = expected_op(lhs.value ^ lhs.is_inverted, rhs.value ^ rhs.is_inverted); @@ -78,10 +82,14 @@ template class BoolTest : public ::testing::Test { } if (!a.is_constant() && !b.is_constant()) { + // The result of a binary op on two witnesses must be a witness EXPECT_TRUE(!c.is_constant()); + // Check that the tags are propagated + EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag); } size_t diff = builder.get_estimated_num_finalized_gates() - num_gates_start; + // An extra gate is created iff both operands are witnesses EXPECT_EQ(diff, static_cast(!a.is_constant() && !b.is_constant())); } } @@ -153,6 +161,12 @@ template class BoolTest : public ::testing::Test { "==", [](const bool_ct& a, const bool_ct& b) { return a == b; }, [](bool a, bool b) { return a == b; }); } + void test_NEQ() + { + test_binary_op( + "==", [](const bool_ct& a, const bool_ct& b) { return a != b; }, [](bool a, bool b) { return a != b; }); + } + void test_implies() { test_binary_op( @@ -168,6 +182,62 @@ template class BoolTest : public ::testing::Test { [](const bool_ct& a, const bool_ct& b) { return a.implies_both_ways(b); }, [](bool a, bool b) { return !a ^ b; }); } + + void test_must_imply() + { + + for (auto& lhs : all_inputs) { + for (auto& rhs : all_inputs) { + Builder builder; + + bool_ct a = create_bool_ct(lhs, &builder); + bool_ct b = create_bool_ct(rhs, &builder); + + if (a.is_constant() && b.is_constant() && !(!a.get_value() || b.get_value())) { +#ifndef NDEBUG + EXPECT_DEATH(a.must_imply(b), R"(\(lhs\.get_value\(\) == rhs\.get_value\(\)\))"); +#endif + } else { + bool result_is_constant = (!a || b).is_constant(); + + size_t num_gates_start = builder.get_estimated_num_finalized_gates(); + + if (!a.is_constant() && !b.is_constant()) { + a.set_origin_tag(submitted_value_origin_tag); + b.set_origin_tag(challenge_origin_tag); + } + a.must_imply(b); + // !a || b + // b = 1 => + bool expected = !(lhs.value ^ lhs.is_inverted) || rhs.value ^ rhs.is_inverted; + + size_t diff = builder.get_estimated_num_finalized_gates() - num_gates_start; + + if (!a.is_constant() && !b.is_constant()) { + EXPECT_EQ(diff, 2); + } + // Due to optimizations, the result of a => b can be a constant, in this case, the the assert_equal + // reduces to an out-of-circuit ASSERT + if (result_is_constant) { + EXPECT_EQ(diff, 0); + } + + if (!result_is_constant && a.is_constant() && !b.is_constant()) { + // we only add gates if the value `true` is not flipped to `false` and we need to add a new + // constant == 1, which happens iff `b` is not inverted. + EXPECT_EQ(diff, static_cast(!b.witness_inverted)); + } + + if (!result_is_constant && !a.is_constant() && b.is_constant()) { + // we only add gates if the value `true` is not flipped to `false` and we need to add a new + // constant == 1, which happens iff `a` is inverted. + EXPECT_EQ(diff, static_cast(a.witness_inverted)); + } + EXPECT_EQ(CircuitChecker::check(builder), expected); + } + } + } + } }; using CircuitTypes = ::testing::Types; @@ -202,6 +272,11 @@ TYPED_TEST(BoolTest, EQ) TestFixture::test_EQ(); } +TYPED_TEST(BoolTest, NEQ) +{ + TestFixture::test_NEQ(); +} + TYPED_TEST(BoolTest, Implies) { TestFixture::test_implies(); @@ -211,6 +286,12 @@ TYPED_TEST(BoolTest, ImpliesBothWays) { TestFixture::test_implies_both_ways(); } + +TYPED_TEST(BoolTest, MustImply) +{ + TestFixture::test_must_imply(); +} + TYPED_TEST(BoolTest, TestBasicOperationsTags) { @@ -350,118 +431,6 @@ TYPED_TEST(BoolTest, Eq) EXPECT_EQ(result, true); } -TYPED_TEST(BoolTest, ImpliesTags) -{ - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - for (size_t j = 0; j < 4; ++j) { - bool lhs_constant = (bool)(j % 2); - bool rhs_constant = (bool)(j > 1 ? true : false); - - for (size_t i = 0; i < 4; ++i) { - bool a_val = (bool)(i % 2); - bool b_val = (bool)(i > 1 ? true : false); - bool_ct a = lhs_constant ? bool_ct(a_val) : (witness_ct(&builder, a_val)); - bool_ct b = rhs_constant ? bool_ct(b_val) : (witness_ct(&builder, b_val)); - if (!(lhs_constant || rhs_constant)) { - a.set_origin_tag(submitted_value_origin_tag); - b.set_origin_tag(challenge_origin_tag); - } - bool_ct c = a.implies(b); - EXPECT_EQ(c.get_value(), !a.get_value() || b.get_value()); - if (!(lhs_constant || rhs_constant)) { - // Tags are merged on implies - EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag); - } - } - } - - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); -} - -TYPED_TEST(BoolTest, ImpliesBothWaysTags) -{ - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - for (size_t j = 0; j < 4; ++j) { - bool lhs_constant = (bool)(j % 2); - bool rhs_constant = (bool)(j > 1 ? true : false); - - for (size_t i = 0; i < 4; ++i) { - bool a_val = (bool)(i % 2); - bool b_val = (bool)(i > 1 ? true : false); - bool_ct a = lhs_constant ? bool_ct(a_val) : (witness_ct(&builder, a_val)); - bool_ct b = rhs_constant ? bool_ct(b_val) : (witness_ct(&builder, b_val)); - if (!(lhs_constant || rhs_constant)) { - a.set_origin_tag(submitted_value_origin_tag); - b.set_origin_tag(challenge_origin_tag); - } - bool_ct c = a.implies_both_ways(b); - EXPECT_EQ(c.get_value(), !(a.get_value() ^ b.get_value())); - - if (!(lhs_constant || rhs_constant)) { - // Tags are merged on implies both ways - EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag); - } - } - } - - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); -} - -TYPED_TEST(BoolTest, MustImply) -{ - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - for (size_t j = 0; j < 4; ++j) { - bool lhs_constant = (bool)(j % 2); - bool rhs_constant = (bool)(j > 1 ? true : false); - - for (size_t i = 4; i < 14; i += 2) { - // If a number is divisible by 2 and 3, it is divisible by 6 - bool two = (bool)(i % 2); - bool three = (bool)(i % 3); - bool six = (bool)(i % 6); - bool a_val = (two && three); - bool b_val = six; - bool_ct a = lhs_constant ? bool_ct(a_val) : (witness_ct(&builder, a_val)); - bool_ct b = rhs_constant ? bool_ct(b_val) : (witness_ct(&builder, b_val)); - a.must_imply(b); - } - } - - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); -} - -TYPED_TEST(BoolTest, MustImplyFails) -{ - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - for (size_t j = 0; j < 3; ++j) { // ignore the case when both lhs and rhs are constants - bool lhs_constant = (bool)(j % 2); - bool rhs_constant = (bool)(j > 1 ? true : false); - - // If a number is divisible by 2 and 3, it is divisible by 6 - // => 8 is not divisible by 3, so it must not be divisible by 6 - const size_t i = 8; - bool a_val = (bool)(i % 2 == 0); - bool b_val = (bool)(i % 6 == 0); - bool_ct a = lhs_constant ? bool_ct(a_val) : (witness_ct(&builder, a_val)); - bool_ct b = rhs_constant ? bool_ct(b_val) : (witness_ct(&builder, b_val)); - a.must_imply(b, "div by 2 does not imply div by 8"); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.err(), "div by 2 does not imply div by 8"); - } -} - TYPED_TEST(BoolTest, MustImplyMultiple) { STDLIB_TYPE_ALIASES From d77149c7ffb7a78a6f8807cc5f926d5516b8845e Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Mon, 30 Jun 2025 17:51:04 +0000 Subject: [PATCH 10/18] normalize test clean-up --- .../stdlib/primitives/bool/bool.test.cpp | 286 ++++++++---------- 1 file changed, 129 insertions(+), 157 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp index 1dfc58f85924..4fff1e41b377 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp @@ -238,6 +238,123 @@ template class BoolTest : public ::testing::Test { } } } + + void test_conditional_assign() + { + for (auto lhs : all_inputs) { + for (auto rhs : all_inputs) { + for (auto predicate : all_inputs) { + Builder builder; + + bool_ct a = create_bool_ct(lhs, &builder); + bool_ct b = create_bool_ct(rhs, &builder); + bool_ct condition = create_bool_ct(predicate, &builder); + + size_t num_gates_start = builder.get_estimated_num_finalized_gates(); + if (!a.is_constant() && !b.is_constant()) { + condition.set_origin_tag(submitted_value_origin_tag); + a.set_origin_tag(challenge_origin_tag); + b.set_origin_tag(next_challenge_tag); + } + + bool_ct result = bool_ct::conditional_assign(condition, a, b); + size_t diff = builder.get_estimated_num_finalized_gates() - num_gates_start; + if (!a.is_constant() && !b.is_constant()) { + EXPECT_EQ(result.get_origin_tag(), first_second_third_merged_tag); + } + bool expected = (condition.get_value()) ? a.get_value() : b.get_value(); + EXPECT_EQ(result.get_value(), expected); + + if (condition.is_constant()) { + EXPECT_EQ(diff, 0); + } + } + } + } + } + void test_normalize() + { + for (auto a_raw : all_inputs) { + auto builder = Builder(); + + bool_ct a = create_bool_ct(a_raw, &builder); + + size_t num_gates_start = builder.get_estimated_num_finalized_gates(); + if (!a.is_constant()) { + a.set_origin_tag(submitted_value_origin_tag); + } + bool_ct c = a.normalize(); + EXPECT_EQ(c.get_value(), a.get_value()); + if (!a.is_constant()) { + EXPECT_EQ(c.get_origin_tag(), submitted_value_origin_tag); + } + EXPECT_EQ(c.witness_inverted, false); + size_t diff = builder.get_estimated_num_finalized_gates() - num_gates_start; + // Note that although `normalize()` returns value, it flips the `witness_inverted` flag of `a` if it was + // `true`. + EXPECT_EQ(diff, static_cast(!a.is_constant() && a_raw.is_inverted)); + EXPECT_TRUE(CircuitChecker::check(builder)); + } + } + + void test_basic_operations_tags() + { + auto builder = Builder(); + + auto gates_before = builder.get_estimated_num_finalized_gates(); + + bool_ct a = witness_ct(&builder, bb::fr::one()); + bool_ct b = witness_ct(&builder, bb::fr::zero()); + + a.set_origin_tag(submitted_value_origin_tag); + b.set_origin_tag(challenge_origin_tag); + + a = a ^ b; // a = 1 + EXPECT_EQ(a.get_value(), 1); + + // Tags are merged on XOR + EXPECT_EQ(a.get_origin_tag(), first_two_merged_tag); + + b = !b; // b = 1 (witness 0) + EXPECT_EQ(b.get_value(), 1); + + // Tag is preserved on NOT + EXPECT_EQ(b.get_origin_tag(), challenge_origin_tag); + + a.set_origin_tag(submitted_value_origin_tag); + + bool_ct d = (a == b); // + EXPECT_EQ(d.get_value(), 1); + + // Tags are merged on == + EXPECT_EQ(d.get_origin_tag(), first_two_merged_tag); + + d = false; // d = 0 + d.set_origin_tag(challenge_origin_tag); + EXPECT_EQ(d.get_value(), 0); + + bool_ct e = a | d; // e = 1 = a + EXPECT_EQ(e.get_value(), 1); + + // Tags are merged on OR + EXPECT_EQ(e.get_origin_tag(), first_two_merged_tag); + + bool_ct f = e ^ b; // f = 0 + EXPECT_EQ(f.get_value(), 0); + + f.set_origin_tag(challenge_origin_tag); + d = (!f) & a; // d = 1 + EXPECT_EQ(d.get_value(), 1); + + // Tags are merged on AND + EXPECT_EQ(d.get_origin_tag(), first_two_merged_tag); + + bool result = CircuitChecker::check(builder); + EXPECT_EQ(result, true); + + auto gates_after = builder.get_estimated_num_finalized_gates(); + EXPECT_EQ(gates_after - gates_before, 6UL); + } }; using CircuitTypes = ::testing::Types; @@ -252,6 +369,11 @@ TYPED_TEST(BoolTest, ConstructFromWitness) { TestFixture::test_construct_from_witness(); } + +TYPED_TEST(BoolTest, Normalization) +{ + TestFixture::test_normalize(); +} TYPED_TEST(BoolTest, XOR) { TestFixture::test_xor(); @@ -292,93 +414,15 @@ TYPED_TEST(BoolTest, MustImply) TestFixture::test_must_imply(); } -TYPED_TEST(BoolTest, TestBasicOperationsTags) +TYPED_TEST(BoolTest, ConditionalAssign) { - - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - auto gates_before = builder.get_estimated_num_finalized_gates(); - - bool_ct a = witness_ct(&builder, bb::fr::one()); - bool_ct b = witness_ct(&builder, bb::fr::zero()); - - a.set_origin_tag(submitted_value_origin_tag); - b.set_origin_tag(challenge_origin_tag); - - a = a ^ b; // a = 1 - EXPECT_EQ(a.get_value(), 1); - - // Tags are merged on XOR - EXPECT_EQ(a.get_origin_tag(), first_two_merged_tag); - - b = !b; // b = 1 (witness 0) - EXPECT_EQ(b.get_value(), 1); - - // Tag is preserved on NOT - EXPECT_EQ(b.get_origin_tag(), challenge_origin_tag); - - a.set_origin_tag(submitted_value_origin_tag); - - bool_ct d = (a == b); // - EXPECT_EQ(d.get_value(), 1); - - // Tags are merged on == - EXPECT_EQ(d.get_origin_tag(), first_two_merged_tag); - - d = false; // d = 0 - d.set_origin_tag(challenge_origin_tag); - EXPECT_EQ(d.get_value(), 0); - - bool_ct e = a | d; // e = 1 = a - EXPECT_EQ(e.get_value(), 1); - - // Tags are merged on OR - EXPECT_EQ(e.get_origin_tag(), first_two_merged_tag); - - bool_ct f = e ^ b; // f = 0 - EXPECT_EQ(f.get_value(), 0); - - f.set_origin_tag(challenge_origin_tag); - d = (!f) & a; // d = 1 - EXPECT_EQ(d.get_value(), 1); - - // Tags are merged on AND - EXPECT_EQ(d.get_origin_tag(), first_two_merged_tag); - - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); - - auto gates_after = builder.get_estimated_num_finalized_gates(); - EXPECT_EQ(gates_after - gates_before, 6UL); + TestFixture::test_conditional_assign(); } -TYPED_TEST(BoolTest, XorTwinConstants) +TYPED_TEST(BoolTest, TestBasicOperationsTags) { - STDLIB_TYPE_ALIASES - auto builder = Builder(); - bool_ct c; - for (size_t i = 0; i < 32; ++i) { - bool_ct a(&builder, (i % 1) == 0); - bool_ct b(&builder, (i % 1) == 1); - c = c ^ a ^ b; - } - c = c ^ bool_ct(witness_ct(&builder, true)); - for (size_t i = 0; i < 32; ++i) { - if (i % 2 == 0) { - bool_ct a = witness_ct(&builder, (bool)(i % 2)); - bool_ct b(&builder, (bool)(i % 3 == 1)); - c = c ^ a ^ b; - } else { - bool_ct a(&builder, (bool)(i % 2)); - bool_ct b = witness_ct(&builder, (bool)(i % 3 == 1)); - c = c ^ a ^ b; - } - } - - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); + TestFixture::test_basic_operations_tags(); } TYPED_TEST(BoolTest, Eq) @@ -507,54 +551,13 @@ TYPED_TEST(BoolTest, MustImplyMultipleFails) } } -TYPED_TEST(BoolTest, ConditionalAssign) -{ - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - for (size_t j = 0; j < 4; ++j) { - bool lhs_constant = (bool)(j % 2); - bool rhs_constant = (bool)(j > 1 ? true : false); - - const uint256_t x = (uint256_t(1) << 128) - 1; - const uint256_t val = engine.get_random_uint256(); - - bool condition = (val % 2 == 0); - bool right = x < val; - bool left = x > val; - bool_ct l_ct = lhs_constant ? bool_ct(left) : (witness_ct(&builder, left)); - bool_ct r_ct = rhs_constant ? bool_ct(right) : (witness_ct(&builder, right)); - bool_ct cond = (witness_ct(&builder, condition)); - - if (!(lhs_constant | rhs_constant)) { - cond.set_origin_tag(submitted_value_origin_tag); - l_ct.set_origin_tag(challenge_origin_tag); - r_ct.set_origin_tag(next_challenge_tag); - } - auto result = bool_ct::conditional_assign(cond, l_ct, r_ct); - - if (!(lhs_constant | rhs_constant)) { - // Tags are merged on conditional assign - EXPECT_EQ(result.get_origin_tag(), first_second_third_merged_tag); - } - - EXPECT_EQ(result.get_value(), condition ? left : right); - } - info("num gates = ", builder.get_estimated_num_finalized_gates()); - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); -} - TYPED_TEST(BoolTest, TestSimpleProof) { STDLIB_TYPE_ALIASES - auto builder = Builder(); + Builder builder; - bool_ct a(&builder); - bool_ct b(&builder); - a = witness_ct(&builder, bb::fr::one()); - b = witness_ct(&builder, bb::fr::zero()); - // bool_ct c(&builder); + bool_ct a = witness_ct(&builder, bb::fr::one()); + bool_ct b = witness_ct(&builder, bb::fr::zero()); a = a ^ b; // a = 1 b = !b; // b = 1 (witness 0) bool_ct c = (a == b); // c = 1 @@ -576,34 +579,3 @@ TYPED_TEST(BoolTest, TestSimpleProof) bool result = CircuitChecker::check(builder); EXPECT_EQ(result, true); } - -TYPED_TEST(BoolTest, Normalize) -{ - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - auto generate_constraints = [&builder](bool value, bool is_constant, bool is_inverted) { - bool_ct a = is_constant ? bool_ct(&builder, value) : witness_ct(&builder, value); - bool_ct b = is_inverted ? !a : a; - if (!is_constant) { - b.set_origin_tag(submitted_value_origin_tag); - } - bool_ct c = b.normalize(); - EXPECT_EQ(c.get_value(), value ^ is_inverted); - if (!is_constant) { - EXPECT_EQ(c.get_origin_tag(), submitted_value_origin_tag); - } - }; - - generate_constraints(false, false, false); - generate_constraints(false, false, true); - generate_constraints(false, true, false); - generate_constraints(false, true, true); - generate_constraints(true, false, false); - generate_constraints(true, false, true); - generate_constraints(true, true, false); - generate_constraints(true, true, true); - - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); -} From 97f010e45c8da8c0ce96245c8fb0b45924ac71e2 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Mon, 30 Jun 2025 18:30:28 +0000 Subject: [PATCH 11/18] delete must imply for vectors --- .../stdlib/primitives/bool/bool.cpp | 64 ---------------- .../stdlib/primitives/bool/bool.hpp | 2 - .../stdlib/primitives/bool/bool.test.cpp | 76 ------------------- 3 files changed, 142 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp index c5eec3488e27..2b3c38e4134a 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp @@ -402,70 +402,6 @@ template void bool_t::must_imply(const bool_t& other implies(other).assert_equal(true, msg); } -/** - * Process many implications all at once, for readablity, and as an optimization. - * @param conds - each pair is a boolean condition that we want to constrain to be "implied", and an error message - * if it is not implied. - * - * Why this works: - * (P => Q) && (P => R) - * <=> (!P || Q) && (!P || R) - * <=> !P || (Q && R) [by distributivity of propositional logic] - * <=> P => (Q && R) [a.k.a. distribution of implication over conjunction] - */ -template -void bool_t::must_imply(const std::vector>& conds) const -{ - // Extract the builder - const bool_t this_bool = *this; - Builder* ctx = this_bool.get_context(); - bool builder_found = (ctx != nullptr); - for (size_t i = 0; i < conds.size(); i++) { - if (!builder_found) { - ctx = conds[i].first.get_context(); - builder_found = (ctx != nullptr); - } - } - - // If no builder is found, all of the bool_t's must be constants. - // In that case, we enforce a static assertion to check must_imply condition holds. - // If all of your inputs do this function are constants and they don't obey a condition, - // this function will panic at those static assertions. - if (!builder_found) { - bool is_const = this_bool.is_constant(); - bool result = !this_bool.get_value(); - bool acc = true; - for (size_t i = 0; i < conds.size(); i++) { - is_const &= conds[i].first.is_constant(); - acc &= conds[i].first.get_value(); - } - result |= acc; - ASSERT(is_const == true); - ASSERT(result == true); - } - - bool_t acc = true; // will accumulate the conjunctions of each condition (i.e. `&&` of each) - const bool this_val = get_value(); - bool error_found = false; - std::string error_msg; - - for (size_t i = 0; i < conds.size(); ++i) { - const bool_t& cond = conds[i].first; - const std::string& msg = conds[i].second; - const bool cond_val = cond.get_value(); - - // If this does NOT imply that, throw the error msg of that condition. - if (this_val && !cond_val && !error_found) { - error_found = true; - error_msg = msg; - } - - acc &= cond; - } - - implies(acc).assert_equal(true, format("multi implication fail: ", error_msg)); -} - // A "double-implication" (<=>), // a.k.a "iff", a.k.a. "biconditional" template bool_t bool_t::implies_both_ways(const bool_t& other) const diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.hpp index 606194131e72..8c070b823ade 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.hpp @@ -106,8 +106,6 @@ template class bool_t { void must_imply(const bool_t& other, std::string const& msg = "bool_t::must_imply") const; - void must_imply(const std::vector>& conds) const; - bool get_value() const { return witness_bool ^ witness_inverted; } bool is_constant() const { return witness_index == IS_CONSTANT; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp index 4fff1e41b377..6a7796dbaa0e 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp @@ -475,82 +475,6 @@ TYPED_TEST(BoolTest, Eq) EXPECT_EQ(result, true); } -TYPED_TEST(BoolTest, MustImplyMultiple) -{ - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - /** - * Define g(x) = 2x + 12 - * if x is divisible by both 4 and 6: - * => g(x) > 0 - * => g(x) is even - * => g(x) >= 12 - * => g(x) is a multiple of 6 - */ - auto g = [](size_t x) { return 2 * x + 12; }; - - for (size_t j = 0; j < 3; ++j) { // ignore when both lhs and rhs are constants - bool lhs_constant = (bool)(j % 2); - bool rhs_constant = (bool)(j > 1 ? true : false); - - for (size_t x = 10; x < 18; x += 2) { - std::vector> conditions; - bool four = (bool)(x % 4 == 0); - bool six = (bool)(x % 6 == 0); - - bool_ct a = lhs_constant ? bool_ct(four) : (witness_ct(&builder, four)); - bool_ct b = rhs_constant ? bool_ct(six) : (witness_ct(&builder, six)); - - auto g_x = g(x); - conditions.push_back(std::make_pair(g_x > 0, "g(x) > 0")); - conditions.push_back(std::make_pair(g_x % 2 == 0, "g(x) is even")); - conditions.push_back(std::make_pair(g_x >= 12, "g(x) >= 12")); - conditions.push_back(std::make_pair(g_x % 6 == 0, "g(x) is a multiple of 6")); - - (a && b).must_imply(conditions); - - if (builder.failed()) { - EXPECT_EQ(builder.err(), "multi implication fail: g(x) is a multiple of 6"); - } else { - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); - } - } - } -} - -TYPED_TEST(BoolTest, MustImplyMultipleFails) -{ - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - /** - * Given x = 15: - * (x > 10) - * => (x > 8) - * => (x > 5) - * ≠> (x > 18) - */ - for (size_t j = 0; j < 2; ++j) { // ignore when both lhs and rhs are constants - bool is_constant = (bool)(j % 2); - - size_t x = 15; - bool main = (bool)(x > 10); - bool_ct main_ct = is_constant ? bool_ct(main) : (witness_ct(&builder, main)); - - std::vector> conditions; - conditions.push_back(std::make_pair(witness_ct(&builder, x > 8), "x > 8")); - conditions.push_back(std::make_pair(witness_ct(&builder, x > 5), "x > 5")); - conditions.push_back(std::make_pair(witness_ct(&builder, x > 18), "x > 18")); - - main_ct.must_imply(conditions); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.err(), "multi implication fail: x > 18"); - } -} - TYPED_TEST(BoolTest, TestSimpleProof) { STDLIB_TYPE_ALIASES From 1cc3bd9f314e8ebd11e6d0e28675ad31343f8e80 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Tue, 1 Jul 2025 11:47:40 +0000 Subject: [PATCH 12/18] final test and docs changes --- .../stdlib/primitives/bool/bool.cpp | 76 ++++++++- .../stdlib/primitives/bool/bool.test.cpp | 158 +++++++++--------- 2 files changed, 145 insertions(+), 89 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp index 2b3c38e4134a..dbfd03671f7f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp @@ -12,16 +12,26 @@ using namespace bb; namespace bb::stdlib { +/** + * @brief Construct a constant `bool_t` object from a `bool` value + */ template bool_t::bool_t(const bool value) : witness_bool(value) {} +/** + * @brief Construct a constant `bool_t` object with a given Builder argument, its value is `false`. + */ template bool_t::bool_t(Builder* parent_context) : context(parent_context) {} +/** + * @brief Construct a `bool_t` object from a witness, note that the value stored at `witness_index` is constrained to be + * 0 or 1. + */ template bool_t::bool_t(const witness_t& value) : context(value.context) @@ -29,18 +39,24 @@ bool_t::bool_t(const witness_t& value) ASSERT((value.witness == bb::fr::zero()) || (value.witness == bb::fr::one()), "bool_t: witness value is not 0 or 1"); witness_index = value.witness_index; + // Constrain x := other.witness by the relation x^2 = x context->create_bool_gate(witness_index); witness_bool = (value.witness == bb::fr::one()); witness_inverted = false; set_free_witness_tag(); } - +/** + * @brief Construct a constant `bool_t` object from a `bool` value with a given Builder argument. + */ template bool_t::bool_t(Builder* parent_context, const bool value) : context(parent_context) , witness_bool(value) {} +/** + * @brief Copy constructor + */ template bool_t::bool_t(const bool_t& other) : context(other.context) @@ -50,6 +66,9 @@ bool_t::bool_t(const bool_t& other) , tag(other.tag) {} +/** + * @brief Move constructor + */ template bool_t::bool_t(bool_t&& other) : context(other.context) @@ -60,6 +79,9 @@ bool_t::bool_t(bool_t&& other) {} +/** + * @brief Assigns a native `bool` to `bool_t` object. + */ template bool_t& bool_t::operator=(const bool other) { context = nullptr; @@ -69,6 +91,9 @@ template bool_t& bool_t::operator=(const bo return *this; } +/** + * @brief Assigns a `bool_t` to a `bool_t` object. + */ template bool_t& bool_t::operator=(const bool_t& other) { context = other.context; @@ -79,6 +104,9 @@ template bool_t& bool_t::operator=(const bo return *this; } +/** + * @brief Assigns a `bool_t` to a `bool_t` object. + */ template bool_t& bool_t::operator=(bool_t&& other) { context = other.context; @@ -88,7 +116,10 @@ template bool_t& bool_t::operator=(bool_t&& tag = other.tag; return *this; } - +/** + * @brief Assigns a `witness_t` to a `bool_t`. As above, he value stored at `witness_index` is constrained to be + * 0 or 1. + */ template bool_t& bool_t::operator=(const witness_t& other) { ASSERT((other.witness == bb::fr::one()) || (other.witness == bb::fr::zero())); @@ -96,11 +127,15 @@ template bool_t& bool_t::operator=(const wi witness_bool = other.witness == bb::fr::one(); witness_index = other.witness_index; witness_inverted = false; + // Constrain x := other.witness by the relation x^2 = x context->create_bool_gate(witness_index); set_free_witness_tag(); return *this; } +/** + * @brief Implements AND in circuit + */ template bool_t bool_t::operator&(const bool_t& other) const { bool_t result(context ? context : other.context); @@ -169,6 +204,9 @@ template bool_t bool_t::operator&(const boo return result; } +/** + * @brief Implements OR in circuit + */ template bool_t bool_t::operator|(const bool_t& other) const { bool_t result(context ? context : other.context); @@ -218,6 +256,9 @@ template bool_t bool_t::operator|(const boo return result; } +/** + * @brief Implements XOR in circuit. + */ template bool_t bool_t::operator^(const bool_t& other) const { bool_t result(context == nullptr ? other.context : context); @@ -266,7 +307,9 @@ template bool_t bool_t::operator^(const boo result.tag = OriginTag(tag, other.tag); return result; } - +/** + * @brief Implements negation in circuit. + */ template bool_t bool_t::operator!() const { bool_t result(*this); @@ -281,6 +324,9 @@ template bool_t bool_t::operator!() const return result; } +/** + * @brief Implements equality operator in circuit. + */ template bool_t bool_t::operator==(const bool_t& other) const { ASSERT(context || other.context || (is_constant() && other.is_constant())); @@ -327,7 +373,9 @@ template bool_t bool_t::operator==(const bo result.tag = OriginTag(tag, other.tag); return result; } - +/** + * @brief Implements the `not equal` operator in circuit. + */ template bool_t bool_t::operator!=(const bool_t& other) const { return operator^(other); @@ -343,6 +391,9 @@ template bool_t bool_t::operator||(const bo return operator|(other); } +/** + * @brief Implements copy constraint for `bool_t` elements. + */ template void bool_t::assert_equal(const bool_t& rhs, std::string const& msg) const { const bool_t lhs = *this; @@ -370,7 +421,9 @@ template void bool_t::assert_equal(const bool_t& rhs } } -// if predicate == true then return lhs, else return rhs +/** + * @brief Implements the ternary operator - if predicate == true then return lhs, else return rhs + */ template bool_t bool_t::conditional_assign(const bool_t& predicate, const bool_t& lhs, @@ -391,19 +444,26 @@ bool_t bool_t::conditional_assign(const bool_t& predi return (predicate && lhs) || (!predicate && rhs); } +/** + * @brief Implements implication operator in circuit. + */ template bool_t bool_t::implies(const bool_t& other) const { // Thanks to negation operator being free, this computation requires at most 1 gate. return (!(*this) || other); // P => Q is equiv. to !P || Q (not(P) or Q). } +/** + * @brief Constrains the (a => b) == true. + */ template void bool_t::must_imply(const bool_t& other, std::string const& msg) const { implies(other).assert_equal(true, msg); } -// A "double-implication" (<=>), -// a.k.a "iff", a.k.a. "biconditional" +/** + @brief Implements a "double-implication" (<=>), a.k.a "iff", a.k.a. "biconditional" + */ template bool_t bool_t::implies_both_ways(const bool_t& other) const { return (!(*this) ^ other); // P <=> Q is equiv. to !(P ^ Q) (not(P xor Q)). @@ -412,7 +472,7 @@ template bool_t bool_t::implies_both_ways(c /** * @brief A bool_t element is **normalized** if `witness_inverted == false`. For a given `*this`, output its normalized * version. - * + * @warning The witness index of *this as well as its `witness_inverted` flag are re-written. */ template bool_t bool_t::normalize() const { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp index 6a7796dbaa0e..187bc49aa662 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp @@ -5,11 +5,6 @@ #include #include -#define STDLIB_TYPE_ALIASES \ - using Builder = TypeParam; \ - using witness_ct = stdlib::witness_t; \ - using bool_ct = stdlib::bool_t; - using namespace bb; #pragma GCC diagnostic ignored "-Wunused-const-variable" @@ -164,7 +159,7 @@ template class BoolTest : public ::testing::Test { void test_NEQ() { test_binary_op( - "==", [](const bool_ct& a, const bool_ct& b) { return a != b; }, [](bool a, bool b) { return a != b; }); + "!=", [](const bool_ct& a, const bool_ct& b) { return a != b; }, [](bool a, bool b) { return a != b; }); } void test_implies() @@ -268,10 +263,13 @@ template class BoolTest : public ::testing::Test { if (condition.is_constant()) { EXPECT_EQ(diff, 0); } + + EXPECT_TRUE(CircuitChecker::check(builder)); } } } } + void test_normalize() { for (auto a_raw : all_inputs) { @@ -297,6 +295,37 @@ template class BoolTest : public ::testing::Test { } } + void test_assert_equal() + { + + for (auto lhs : all_inputs) { + for (auto rhs : all_inputs) { + + Builder builder; + + bool_ct a = create_bool_ct(lhs, &builder); + bool_ct b = create_bool_ct(rhs, &builder); + + bool failed = a.get_value() != b.get_value(); + + if (!a.is_constant() && !b.is_constant()) { + a.assert_equal(b); + // CircuitChecker is not verifying the permutation relation + EXPECT_EQ(builder.failed(), failed); + } else if (!a.is_constant() || !b.is_constant()) { + a.assert_equal(b); + EXPECT_EQ(CircuitChecker::check(builder), !failed); + } else { + if (failed) { +#ifndef NDEBUG + EXPECT_DEATH(a.assert_equal(b), R"(\(lhs\.get_value\(\) == rhs\.get_value\(\)\))"); +#endif + } + } + } + } + } + void test_basic_operations_tags() { auto builder = Builder(); @@ -355,6 +384,44 @@ template class BoolTest : public ::testing::Test { auto gates_after = builder.get_estimated_num_finalized_gates(); EXPECT_EQ(gates_after - gates_before, 6UL); } + + // Check that (a && (b || c)) ^ (d => f) <=> ((a && b) || (a && c)) ^ (!d || f)) for all inputs. + void test_simple_proof() + { + for (const auto& a_input : all_inputs) { + for (const auto& b_input : all_inputs) { + for (const auto& c_input : all_inputs) { + for (const auto& d_input : all_inputs) { + for (const auto& f_input : all_inputs) { + Builder builder; + + // Construct bool_cts from inputs + bool_ct a = create_bool_ct(a_input, &builder); + bool_ct b = create_bool_ct(b_input, &builder); + bool_ct c = create_bool_ct(c_input, &builder); + bool_ct d = create_bool_ct(d_input, &builder); + bool_ct f = create_bool_ct(f_input, &builder); + + // === Formula 1 === + bool_ct lhs = (a && (b || c)) ^ (d.implies(f)); + bool_ct rhs = ((a && b) || (a && c)) ^ (!d || f); + + // Equivalence check + bool_ct equivalent = lhs.implies_both_ways(rhs); + if (!equivalent.get_value()) { + info("FAIL:"); + info("a: ", a.get_value(), " b: ", b.get_value(), " c: ", c.get_value()); + info("d: ", d.get_value(), " f: ", f.get_value()); + } + + EXPECT_EQ(equivalent.get_value(), true); + EXPECT_TRUE(CircuitChecker::check(builder)); + } + } + } + } + } + } }; using CircuitTypes = ::testing::Types; @@ -421,85 +488,14 @@ TYPED_TEST(BoolTest, ConditionalAssign) TYPED_TEST(BoolTest, TestBasicOperationsTags) { - TestFixture::test_basic_operations_tags(); } -TYPED_TEST(BoolTest, Eq) +TYPED_TEST(BoolTest, TestSimpleProof) { - STDLIB_TYPE_ALIASES - auto builder = Builder(); - - bool a_alt[32]; - bool b_alt[32]; - bool c_alt[32]; - bool d_alt[32]; - for (size_t i = 0; i < 32; ++i) { - if (i % 2 == 0) { - a_alt[i] = bool(i % 2); - b_alt[i] = false; - c_alt[i] = a_alt[i] ^ b_alt[i]; - d_alt[i] = a_alt[i] == c_alt[i]; - } else { - a_alt[i] = true; - b_alt[i] = false; - c_alt[i] = false; - d_alt[i] = false; - } - } - bool_ct a[32]; - bool_ct b[32]; - bool_ct c[32]; - bool_ct d[32]; - for (size_t i = 0; i < 32; ++i) { - if (i % 2 == 0) { - a[i] = witness_ct(&builder, (bool)(i % 2)); - b[i] = witness_ct(&builder, (bool)(0)); - c[i] = a[i] ^ b[i]; - d[i] = a[i] == c[i]; - } else { - a[i] = witness_ct(&builder, (bool)(1)); - b[i] = witness_ct(&builder, (bool)(0)); - c[i] = a[i] & b[i]; - d[i] = a[i] == c[i]; - } - } - for (size_t i = 0; i < 32; ++i) { - EXPECT_EQ(a[i].get_value(), a_alt[i]); - EXPECT_EQ(b[i].get_value(), b_alt[i]); - EXPECT_EQ(c[i].get_value(), c_alt[i]); - EXPECT_EQ(d[i].get_value(), d_alt[i]); - } - - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); + TestFixture::test_simple_proof(); } - -TYPED_TEST(BoolTest, TestSimpleProof) +TYPED_TEST(BoolTest, AssertEqual) { - STDLIB_TYPE_ALIASES - Builder builder; - - bool_ct a = witness_ct(&builder, bb::fr::one()); - bool_ct b = witness_ct(&builder, bb::fr::zero()); - a = a ^ b; // a = 1 - b = !b; // b = 1 (witness 0) - bool_ct c = (a == b); // c = 1 - bool_ct d(&builder); // d = ? - d = false; // d = 0 - bool_ct e = a | d; // e = 1 = a - bool_ct f = e ^ b; // f = 0 - d = (!f) & a; // d = 1 - for (size_t i = 0; i < 64; ++i) { - a = witness_ct(&builder, (bool)(i % 2)); - b = witness_ct(&builder, (bool)(i % 3 == 1)); - c = a ^ b; - a = b ^ c; - c = a; - a = b; - f = b; - } - - bool result = CircuitChecker::check(builder); - EXPECT_EQ(result, true); + TestFixture::test_assert_equal(); } From e9173d34db78cbab8f0c5672428cb06296cd8e28 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Thu, 3 Jul 2025 15:15:58 +0000 Subject: [PATCH 13/18] address review comments --- .../cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp index dbfd03671f7f..28a378e972b2 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp @@ -76,7 +76,6 @@ bool_t::bool_t(bool_t&& other) , witness_inverted(other.witness_inverted) , witness_index(other.witness_index) , tag(other.tag) - {} /** @@ -267,7 +266,6 @@ template bool_t bool_t::operator^(const boo result.witness_bool = (witness_bool ^ witness_inverted) ^ (other.witness_bool ^ other.witness_inverted); bb::fr value = result.witness_bool ? bb::fr::one() : bb::fr::zero(); - result.witness_inverted = false; if (!is_constant() && !other.is_constant()) { result.witness_index = context->add_variable(value); @@ -402,10 +400,12 @@ template void bool_t::assert_equal(const bool_t& rhs if (lhs.is_constant() && rhs.is_constant()) { ASSERT(lhs.get_value() == rhs.get_value()); } else if (lhs.is_constant()) { + ASSERT(!lhs.witness_inverted); // if rhs is inverted, flip the value of the lhs constant const bool lhs_value = rhs.witness_inverted ? !lhs.witness_bool : lhs.witness_bool; ctx->assert_equal_constant(rhs.witness_index, lhs_value, msg); } else if (rhs.is_constant()) { + ASSERT(!rhs.witness_inverted); // if lhs is inverted, flip the value of the rhs constant const bool rhs_value = lhs.witness_inverted ? !rhs.witness_bool : rhs.witness_bool; ctx->assert_equal_constant(lhs.witness_index, rhs_value, msg); From c937a167911b4d6912328a4398de34858a38390f Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Thu, 3 Jul 2025 15:55:20 +0000 Subject: [PATCH 14/18] fix implies both ways --- .../cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp | 2 +- .../cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp index 28a378e972b2..5bd05826fede 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.cpp @@ -466,7 +466,7 @@ template void bool_t::must_imply(const bool_t& other */ template bool_t bool_t::implies_both_ways(const bool_t& other) const { - return (!(*this) ^ other); // P <=> Q is equiv. to !(P ^ Q) (not(P xor Q)). + return !((*this) ^ other); // P <=> Q is equiv. to !(P ^ Q) (not(P xor Q)). } /** diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp index 187bc49aa662..fdbd6ade42f4 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bool/bool.test.cpp @@ -175,7 +175,7 @@ template class BoolTest : public ::testing::Test { test_binary_op( "<=>", [](const bool_ct& a, const bool_ct& b) { return a.implies_both_ways(b); }, - [](bool a, bool b) { return !a ^ b; }); + [](bool a, bool b) { return !(a ^ b); }); } void test_must_imply() From fc27f484976e50d42da85ba0de3f8944b0d0fa2f Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Thu, 3 Jul 2025 16:02:26 +0000 Subject: [PATCH 15/18] fix vks --- .../cpp/scripts/test_civc_standalone_vks_havent_changed.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/barretenberg/cpp/scripts/test_civc_standalone_vks_havent_changed.sh b/barretenberg/cpp/scripts/test_civc_standalone_vks_havent_changed.sh index 006de3824f46..ea3ebd61deb5 100755 --- a/barretenberg/cpp/scripts/test_civc_standalone_vks_havent_changed.sh +++ b/barretenberg/cpp/scripts/test_civc_standalone_vks_havent_changed.sh @@ -11,7 +11,7 @@ cd .. # - Generate a hash for versioning: sha256sum bb-civc-inputs.tar.gz # - Upload the compressed results: aws s3 cp bb-civc-inputs.tar.gz s3://aztec-ci-artifacts/protocol/bb-civc-inputs-[hash(0:8)].tar.gz # Note: In case of the "Test suite failed to run ... Unexpected token 'with' " error, need to run: docker pull aztecprotocol/build:3.0 -pinned_civc_inputs_url="https://aztec-ci-artifacts.s3.us-east-2.amazonaws.com/protocol/bb-civc-inputs-e7dd76ac.tar.gz" +pinned_civc_inputs_url="https://aztec-ci-artifacts.s3.us-east-2.amazonaws.com/protocol/bb-civc-inputs-32c5d30b.tar.gz" # For easily rerunning the inputs generation if [[ "${1:-}" == "--update-inputs" ]]; then @@ -20,7 +20,7 @@ if [[ "${1:-}" == "--update-inputs" ]]; then # 1) Generate new inputs echo "Running bootstrap to generate new IVC inputs..." - + ../bootstrap.sh # bootstrap bb ../../noir-projects/noir-protocol-circuits/bootstrap.sh # bootstrap noir-protocol-circuits ../../yarn-project/bootstrap.sh # bootstrap yarn-project From 861a6c90139ac4e0c83c99fed143dcc52b448488 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Thu, 3 Jul 2025 16:11:30 +0000 Subject: [PATCH 16/18] noir format --- .../contracts/fees/fpc_contract/src/main.nr | 2 +- .../src/private_kernel_inner.nr | 40 +++++++++---------- .../src/private_kernel_reset.nr | 14 +++---- .../reset_output_validator_builder/mod.nr | 36 ++++++++--------- .../note_hash_read_request_hints_builder.nr | 6 +-- .../nullifier_read_request_hints_builder.nr | 6 +-- ...t_split_sorted_transformed_value_arrays.nr | 4 +- 7 files changed, 53 insertions(+), 55 deletions(-) diff --git a/noir-projects/noir-contracts/contracts/fees/fpc_contract/src/main.nr b/noir-projects/noir-contracts/contracts/fees/fpc_contract/src/main.nr index 08854e8e6077..8bdf54e7c0bf 100644 --- a/noir-projects/noir-contracts/contracts/fees/fpc_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fees/fpc_contract/src/main.nr @@ -159,7 +159,7 @@ pub contract FPC { Token::at(config.accepted_asset) .transfer_in_public(context.msg_sender(), context.this_address(), max_fee, authwit_nonce ) - .enqueue(&mut context); + .enqueue(&mut context); // docs:end:public_call context.set_as_fee_payer(); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr index d8829244187f..ebd17f8f2f87 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr @@ -60,30 +60,28 @@ impl PrivateKernelInnerCircuitPrivateInputs { start_private_call_stack_length - 1]; - private_call_data_validator.verify_proof(false /* is_first_app */); - private_call_data_validator.validate_against_call_request(call_request); - private_call_data_validator.validate_against_previous_kernel( + private_call_data_validator.verify_proof(false /* is_first_app */); + private_call_data_validator.validate_against_call_request(call_request); + private_call_data_validator.validate_against_previous_kernel(self.previous_kernel.public_inputs); + + // Generate output. + // Safety: The output is validated below by PrivateKernelCircuitOutputValidator. + let output = unsafe { self.generate_output() }; + + private_call_data_validator.validate_common( + output.end.note_hashes, + self.previous_kernel.public_inputs.constants.protocol_contract_tree_root, + ); + + // Validate output. + if dep::types::validate::should_validate_output() { + PrivateKernelCircuitOutputValidator::new(output).validate_as_inner_call( self.previous_kernel.public_inputs, + self.private_call, ); - - // Generate output. - // Safety: The output is validated below by PrivateKernelCircuitOutputValidator. - let output = unsafe { self.generate_output() }; - - private_call_data_validator.validate_common( - output.end.note_hashes, - self.previous_kernel.public_inputs.constants.protocol_contract_tree_root, - ); - - // Validate output. - if dep::types::validate::should_validate_output() { - PrivateKernelCircuitOutputValidator::new(output).validate_as_inner_call( - self.previous_kernel.public_inputs, - self.private_call, - ); - } - output } + output +} } mod tests { diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr index 314cfc29604d..8a3c7a715af2 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr @@ -225,14 +225,14 @@ mod tests { TransientDataIndexHint::nada(MAX_NULLIFIERS_PER_TX, MAX_NOTE_HASHES_PER_TX); TRANSIENT_DATA_SQUASHING_HINTS_LEN ], - note_hash_read_request_hints_builder: NoteHashReadRequestHintsBuilder::new(), - nullifier_read_request_hints_builder: NullifierReadRequestHintsBuilder::new(), - key_validation_hints: BoundedVec::new(), - validation_requests_split_counter: 0, - dimensions, - padded_side_effects: PaddedSideEffects::empty(), - } + note_hash_read_request_hints_builder: NoteHashReadRequestHintsBuilder::new(), + nullifier_read_request_hints_builder: NullifierReadRequestHintsBuilder::new(), + key_validation_hints: BoundedVec::new(), + validation_requests_split_counter: 0, + dimensions, + padded_side_effects: PaddedSideEffects::empty(), } + } pub fn with_siloing(&mut self) -> Self { self.dimensions.NOTE_HASH_SILOING_AMOUNT = NOTE_HASH_SILOING_AMOUNT; diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/reset_output_validator_builder/mod.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/reset_output_validator_builder/mod.nr index 5b2b98267225..723f90f681d8 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/reset_output_validator_builder/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/reset_output_validator_builder/mod.nr @@ -63,25 +63,25 @@ impl ResetOutputValidatorBuilder { TRANSIENT_DATA_SQUASHING_HINTS_LEN ]; - ResetOutputValidatorBuilder { - output, - previous_kernel, - note_hash_read_request_hints_builder, - nullifier_read_request_hints_builder, - key_validation_hints, - transient_data_index_hints, - pending_note_hash_read_amount: 0, - settled_note_hash_read_amount: 0, - pending_nullifier_read_amount: 0, - settled_nullifier_read_amount: 0, - key_validation_amount: 0, - transient_data_squashing_amount: 0, - note_hash_siloing_amount: 0, - nullifier_siloing_amount: 0, - private_log_siloing_amount: 0, - padded_side_effects: PaddedSideEffects::empty(), - } + ResetOutputValidatorBuilder { + output, + previous_kernel, + note_hash_read_request_hints_builder, + nullifier_read_request_hints_builder, + key_validation_hints, + transient_data_index_hints, + pending_note_hash_read_amount: 0, + settled_note_hash_read_amount: 0, + pending_nullifier_read_amount: 0, + settled_nullifier_read_amount: 0, + key_validation_amount: 0, + transient_data_squashing_amount: 0, + note_hash_siloing_amount: 0, + nullifier_siloing_amount: 0, + private_log_siloing_amount: 0, + padded_side_effects: PaddedSideEffects::empty(), } + } pub fn get_validation_request_processor( self, diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/note_hash_read_request_hints_builder.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/note_hash_read_request_hints_builder.nr index 1659379d9223..a1382fe36133 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/note_hash_read_request_hints_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/note_hash_read_request_hints_builder.nr @@ -23,10 +23,10 @@ impl NoteHashReadRequest NoteHashSettledReadHint::skip(MAX_NOTE_HASH_READ_REQUESTS_PER_TX); SETTLED_HINTS_LEN ], - 0, - ), - } + 0, + ), } + } pub fn add_pending_read_hint(&mut self, read_request_index: u32, note_hash_index: u32) { let hint_index = self.pending_read_hints.len(); diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/nullifier_read_request_hints_builder.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/nullifier_read_request_hints_builder.nr index b337671abd12..38e946f36d35 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/nullifier_read_request_hints_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/nullifier_read_request_hints_builder.nr @@ -23,10 +23,10 @@ impl NullifierReadReques NullifierSettledReadHint::skip(MAX_NULLIFIER_READ_REQUESTS_PER_TX); SETTLED_HINTS_LEN ], - 0, - ), - } + 0, + ), } + } pub fn add_pending_read_hint(&mut self, read_request_index: u32, nullifier_index: u32) { let hint_index = self.pending_read_hints.len(); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays.nr index 5c5952252a1c..93106d54403e 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays.nr @@ -267,9 +267,9 @@ mod tests { { original_index = i; } - } - self.hints.sorted_indexes[original_index] = new_index; } + self.hints.sorted_indexes[original_index] = new_index; + } pub fn execute(self) { if self.ascending { From 9abef4386e5d1a345dca006a29c985dfd2d5583d Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Fri, 4 Jul 2025 09:19:17 +0000 Subject: [PATCH 17/18] vk --- .../cpp/scripts/test_civc_standalone_vks_havent_changed.sh | 2 +- barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/barretenberg/cpp/scripts/test_civc_standalone_vks_havent_changed.sh b/barretenberg/cpp/scripts/test_civc_standalone_vks_havent_changed.sh index b3b646b64c0d..eea7d277865c 100755 --- a/barretenberg/cpp/scripts/test_civc_standalone_vks_havent_changed.sh +++ b/barretenberg/cpp/scripts/test_civc_standalone_vks_havent_changed.sh @@ -11,7 +11,7 @@ cd .. # - Generate a hash for versioning: sha256sum bb-civc-inputs.tar.gz # - Upload the compressed results: aws s3 cp bb-civc-inputs.tar.gz s3://aztec-ci-artifacts/protocol/bb-civc-inputs-[hash(0:8)].tar.gz # Note: In case of the "Test suite failed to run ... Unexpected token 'with' " error, need to run: docker pull aztecprotocol/build:3.0 -pinned_civc_inputs_url="https://aztec-ci-artifacts.s3.us-east-2.amazonaws.com/protocol/bb-civc-inputs-32c5d30b.tar.gz" +pinned_civc_inputs_url="https://aztec-ci-artifacts.s3.us-east-2.amazonaws.com/protocol/bb-civc-inputs-4d015f03.tar.gz" # For easily rerunning the inputs generation if [[ "${1:-}" == "--update-inputs" ]]; then diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp index 7e90f335f091..819dd5db3325 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp @@ -97,6 +97,7 @@ template void OinkProver::execute_preamble_ro auto public_input_i = proving_key->proving_key.public_inputs[i]; transcript->send_to_verifier(domain_separator + "public_input_" + std::to_string(i), public_input_i); } + info(builder->get_estimated) } /** From a9b70f855fc5a62527c5710ce063d28880eea6aa Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Fri, 4 Jul 2025 09:22:08 +0000 Subject: [PATCH 18/18] revert oink --- barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp index 819dd5db3325..7e90f335f091 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp @@ -97,7 +97,6 @@ template void OinkProver::execute_preamble_ro auto public_input_i = proving_key->proving_key.public_inputs[i]; transcript->send_to_verifier(domain_separator + "public_input_" + std::to_string(i), public_input_i); } - info(builder->get_estimated) } /**