Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
logic.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Suyash], commit: e4712cda8def49d75fbba2d361625fc5e21945f5 }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#include "logic.hpp"
8#include "../circuit_builders/circuit_builders.hpp"
9#include "../plookup/plookup.hpp"
13#include <cstddef>
14
15namespace bb::stdlib {
16
31template <typename Builder>
33 field_pt& a,
34 field_pt& b,
35 size_t num_bits,
36 bool is_xor_gate,
37 const std::function<std::pair<uint256_t, uint256_t>(uint256_t, uint256_t, size_t)>& get_chunk)
38{
39 // ensure the number of bits doesn't exceed field size and is not negative
41 BB_ASSERT_GT(num_bits, 0U);
42
43 if (a.is_constant() && b.is_constant()) {
44 uint256_t a_native = static_cast<uint256_t>(a.get_value());
45 uint256_t b_native = static_cast<uint256_t>(b.get_value());
47 a_native.get_msb(), num_bits - 1, "field_t: Left operand in logic gate exceeds specified bit length");
49 b_native.get_msb(), num_bits - 1, "field_t: Right operand in logic gate exceeds specified bit length");
50
51 uint256_t result_native = is_xor_gate ? (a_native ^ b_native) : (a_native & b_native);
52 field_pt result(result_native);
53 result.set_origin_tag(OriginTag(a.get_origin_tag(), b.get_origin_tag()));
54 return result;
55 }
56
57 if (a.is_constant() && !b.is_constant()) {
58 Builder* ctx = b.get_context();
59 uint256_t a_native(a.get_value());
60 field_pt a_witness = field_pt::from_witness_index(ctx, ctx->put_constant_variable(a_native));
61 a_witness.set_origin_tag(a.get_origin_tag());
62 return create_logic_constraint(a_witness, b, num_bits, is_xor_gate, get_chunk);
63 }
64
65 if (!a.is_constant() && b.is_constant()) {
66 Builder* ctx = a.get_context();
67 uint256_t b_native(b.get_value());
68 field_pt b_witness = field_pt::from_witness_index(ctx, ctx->put_constant_variable(b_native));
69 b_witness.set_origin_tag(b.get_origin_tag());
70 return create_logic_constraint(a, b_witness, num_bits, is_xor_gate, get_chunk);
71 }
72
73 Builder* ctx = validate_context<Builder>(a.get_context(), b.get_context());
74
75 // We slice the input values into 32-bit chunks, and then use a multi-table lookup to compute the AND or XOR
76 // of each chunk. Since we perform the lookup from 32-bit multi-tables, the lookup operation implicitly enforces a
77 // 32-bit range constraint on each chunk. However, if `num_bits` is not a multiple of 32, the last chunk will be
78 // smaller than 32 bits. Therefore, the last chunk needs to be explicitly range-constrained to ensure it is in the
79 // correct range. The result is then reconstructed from the chunks, and checked against the original value.
80 const size_t num_chunks = (num_bits / 32) + ((num_bits % 32 == 0) ? 0 : 1);
81 auto left((uint256_t)a.get_value());
82 auto right((uint256_t)b.get_value());
83
84 field_pt a_accumulator(bb::fr::zero());
85 field_pt b_accumulator(bb::fr::zero());
86
87 field_pt res(ctx, 0);
88 for (size_t i = 0; i < num_chunks; ++i) {
89 size_t chunk_size = (i != num_chunks - 1) ? 32 : num_bits - i * 32;
90 auto [left_chunk, right_chunk] = get_chunk(left, right, chunk_size);
91
92 field_pt a_chunk = witness_pt(ctx, left_chunk);
93 field_pt b_chunk = witness_pt(ctx, right_chunk);
94 const auto multi_table_id = is_xor_gate ? plookup::MultiTableId::UINT32_XOR : plookup::MultiTableId::UINT32_AND;
95 field_pt result_chunk = stdlib::plookup_read<Builder>::read_from_2_to_1_table(multi_table_id, a_chunk, b_chunk);
96
97 auto scaling_factor = uint256_t(1) << (32 * i);
98 a_accumulator += a_chunk * scaling_factor;
99 b_accumulator += b_chunk * scaling_factor;
100
101 if (chunk_size != 32) {
102 // If the chunk is smaller than 32 bits, we need to explicitly range constrain it.
103 a_chunk.create_range_constraint(chunk_size, "stdlib logic: bad range on final chunk of left operand");
104 b_chunk.create_range_constraint(chunk_size, "stdlib logic: bad range on final chunk of right operand");
105 }
106
107 res += result_chunk * scaling_factor;
108
109 left = left >> 32;
110 right = right >> 32;
111 }
112
113 a.assert_equal(a_accumulator, "stdlib logic: failed to reconstruct left operand");
114 b.assert_equal(b_accumulator, "stdlib logic: failed to reconstruct right operand");
115
116 res.set_origin_tag(OriginTag(a.get_origin_tag(), b.get_origin_tag()));
117 return res;
118}
119template class logic<bb::UltraCircuitBuilder>;
120template class logic<bb::MegaCircuitBuilder>;
121} // namespace bb::stdlib
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:113
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:158
constexpr uint64_t get_msb() const
static field_t from_witness_index(Builder *ctx, uint32_t witness_index)
Definition field.cpp:67
void create_range_constraint(size_t num_bits, std::string const &msg="field_t::range_constraint") const
Let x = *this.normalize(), constrain x.v < 2^{num_bits}.
Definition field.cpp:921
void set_origin_tag(const OriginTag &new_tag) const
Definition field.hpp:358
static field_pt create_logic_constraint(field_pt &a, field_pt &b, size_t num_bits, bool is_xor_gate, const std::function< std::pair< uint256_t, uint256_t >(uint256_t, uint256_t, size_t)> &get_chunk=[](uint256_t left, uint256_t right, size_t chunk_size) { uint256_t left_chunk=left &((uint256_t(1)<< chunk_size) - 1);uint256_t right_chunk=right &((uint256_t(1)<< chunk_size) - 1);return std::make_pair(left_chunk, right_chunk);})
A logical AND or XOR over a variable number of bits.
Definition logic.cpp:32
static field_pt read_from_2_to_1_table(const plookup::MultiTableId id, const field_pt &key_a, const field_pt &key_b)
Definition plookup.cpp:83
FF a
FF b
stdlib::witness_t< bb::UltraCircuitBuilder > witness_pt
constexpr size_t MAX_NO_WRAP_INTEGER_BIT_LENGTH
Definition grumpkin.hpp:16
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static constexpr field zero()