diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/uint/arithmetic.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/uint/arithmetic.cpp index b51fb1f47df0..dcc0329b65d8 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/uint/arithmetic.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/uint/arithmetic.cpp @@ -48,7 +48,7 @@ uint uint::operator+(const uint& other) const uint result(ctx); result.witness_index = gate.c; - result.normalize(); + result.witness_status = WitnessStatus::WEAK_NORMALIZED; return result; } @@ -94,7 +94,7 @@ uint uint::operator-(const uint& other) const uint result(ctx); result.witness_index = gate.c; - result.normalize(); + result.witness_status = WitnessStatus::WEAK_NORMALIZED; return result; } @@ -139,8 +139,9 @@ uint uint::operator*(const uint& other) const ctx->decompose_into_default_range(gate.d, width); uint result(ctx); + result.accumulators = constrain_accumulators(ctx, gate.c); result.witness_index = gate.c; - result.normalize(); + result.witness_status = WitnessStatus::OK; return result; } @@ -247,11 +248,13 @@ std::pair, uint> uint::d ctx->decompose_into_default_range(delta_idx, width); uint quotient(ctx); quotient.witness_index = quotient_idx; - quotient.normalize(); + quotient.accumulators = constrain_accumulators(ctx, quotient.witness_index); + quotient.witness_status = WitnessStatus::OK; uint remainder(ctx); remainder.witness_index = remainder_idx; - remainder.normalize(); + remainder.accumulators = constrain_accumulators(ctx, remainder.witness_index); + remainder.witness_status = WitnessStatus::OK; return std::make_pair(quotient, remainder); } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/uint/uint.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/uint/uint.cpp index a299cf8eb4a6..2fbd81f9d756 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/uint/uint.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/uint/uint.cpp @@ -20,6 +20,7 @@ std::vector uint::constrain_accumulators(Builder* con template uint::uint(const witness_t& witness) : context(witness.context) + , witness_status(WitnessStatus::OK) { if (witness.witness_index == IS_CONSTANT) { additive_constant = witness.witness; @@ -34,6 +35,7 @@ template uint::uint(const field_t& value) : context(value.context) , additive_constant(0) + , witness_status(WitnessStatus::OK) { if (value.witness_index == IS_CONSTANT) { additive_constant = value.additive_constant; @@ -49,6 +51,7 @@ template uint::uint(Builder* builder, const uint256_t& value) : context(builder) , additive_constant(value) + , witness_status(WitnessStatus::OK) , accumulators() , witness_index(IS_CONSTANT) {} @@ -57,6 +60,7 @@ template uint::uint(const uint256_t& value) : context(nullptr) , additive_constant(value) + , witness_status(WitnessStatus::OK) , accumulators() , witness_index(IS_CONSTANT) {} @@ -65,6 +69,7 @@ template uint::uint(const byte_array& other) : context(other.get_context()) , additive_constant(0) + , witness_status(WitnessStatus::WEAK_NORMALIZED) , accumulators() , witness_index(IS_CONSTANT) { @@ -83,9 +88,6 @@ uint::uint(const byte_array& other) } else { witness_index = accumulator.witness_index; } - - // We need to constrain the accumulators, so we normalize here. - normalize(); } template @@ -97,6 +99,7 @@ template uint::uint(Builder* parent_context, const std::vector>& wires) : context(parent_context) , additive_constant(0) + , witness_status(WitnessStatus::WEAK_NORMALIZED) , accumulators() , witness_index(IS_CONSTANT) { @@ -114,15 +117,13 @@ uint::uint(Builder* parent_context, const std::vector uint::uint(const uint& other) : context(other.context) , additive_constant(other.additive_constant) + , witness_status(other.witness_status) , accumulators(other.accumulators) , witness_index(other.witness_index) {} @@ -131,6 +132,7 @@ template uint::uint(uint&& other) : context(other.context) , additive_constant(other.additive_constant) + , witness_status(other.witness_status) , accumulators(other.accumulators) , witness_index(other.witness_index) {} @@ -139,6 +141,7 @@ template uint& uint uint& uint uint uint uint256_t uint::ge if (!context || is_constant()) { return additive_constant; } + return (uint256_t(context->get_variable(witness_index))) & MASK; +} - const uint256_t witness_value = context->get_variable(witness_index); - ASSERT(witness_value.get_msb() < width, "uint::get_value(): witness value exceeds type width"); - - return witness_value & MASK; +template uint256_t uint::get_unbounded_value() const +{ + if (!context || is_constant()) { + return additive_constant; + } + return (uint256_t(context->get_variable(witness_index))); } template bool_t uint::at(const size_t bit_index) const @@ -195,6 +205,9 @@ template bool_t uint(context, get_value().get_bit(bit_index)); } + if (witness_status != WitnessStatus::OK) { + normalize(); + } const uint64_t slice_bit_position = bit_index % bits_per_limb; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/uint/uint.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/uint/uint.hpp index e2929eccbb1b..7c68f3de436a 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/uint/uint.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/uint/uint.hpp @@ -140,11 +140,15 @@ template class uint { uint256_t get_additive_constant() const { return additive_constant; } std::vector get_accumulators() const { return accumulators; } + uint256_t get_unbounded_value() const; protected: Builder* context; + enum WitnessStatus { OK, NOT_NORMALIZED, WEAK_NORMALIZED }; + mutable uint256_t additive_constant; + mutable WitnessStatus witness_status; // N.B. Not an accumulator! Contains 6-bit slices of input mutable std::vector accumulators; diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.cpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.cpp index 09d0e2057124..6db99da01004 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.cpp @@ -510,11 +510,18 @@ void UltraCircuitBuilder_::create_balanced_add_gate(const add_qu } check_selector_length_consistency(); ++this->num_gates; - - // Range constrain the 4-th wire to {0, 1}. Since the inputs being added never exceed (2^x - 1) - // during uintx arithmetic, we can safely use a 1-bit range check here. In other words, we do not - // allow lazy uintx addition. - create_new_range_constraint(in.d, 1); + // Why 3? TODO: return to this + // The purpose of this gate is to do enable lazy 32-bit addition. + // Consider a + b = c mod 2^32 + // We want the 4th wire to represent the quotient: + // w1 + w2 = w4 * 2^32 + w3 + // If we allow this overflow 'flag' to range from 0 to 3, instead of 0 to 1, + // we can get away with chaining a few addition operations together with basic add gates, + // before having to use this gate. + // (N.B. a larger value would be better, the value '3' is for Turbo backwards compatibility. + // In Turbo this method uses a custom gate, + // where we were limited to a 2-bit range check by the degree of the custom gate identity. + create_new_range_constraint(in.d, 3); } /** * @brief Create a multiplication gate with q_m * a * b + q_3 * c + q_const = 0