Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bitwise_trace.cpp
Go to the documentation of this file.
2
3#include <cstddef>
4#include <cstdint>
5#include <memory>
6#include <ranges>
7#include <stdexcept>
8
14
15namespace bb::avm2::tracegen {
16
19{
20 using C = Column;
21 // Important to not set last to 1 in the first row if there are no events. Otherwise, the skippable condition
22 // would be skipped wrongly as the sub-relation last * (1 - last) = 0 cannot be satisified (after the
23 // randomization process happening in the sumcheck protocol).
24 if (!events.empty()) {
25 // We activate last selector in the first row.
26 trace.set(C::bitwise_last, 0, 1);
27 }
28
29 // Precomputed inverses ranges from 0 to 16. (for columns bitwise_ctr_inv, bitwise_ctr_min_one_inv)
30 std::array<FF, 17> precomputed_inverses = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
31 FF::batch_invert(precomputed_inverses);
32
33 uint32_t row = 1;
34 for (const auto& event : events) {
35 auto tag = event.a.get_tag();
36
37 // We start with full inputs and output and we shift
38 // them byte-per-byte to the right.
39 uint128_t input_a = static_cast<uint128_t>(event.a.as_ff());
40 uint128_t input_b = static_cast<uint128_t>(event.b.as_ff());
41 uint128_t output_c = event.res;
42
43 // Error Handling, check tag a is FF or tag a != tag b
44 bool is_tag_ff = event.a.get_tag() == MemoryTag::FF;
45 bool is_tag_mismatch = event.a.get_tag() != event.b.get_tag();
46 // For tag_a != FF
47 // Rely below on MemoryTag::FF being 0
48 static_assert(static_cast<uint8_t>(MemoryTag::FF) == 0);
49 const uint8_t tag_a_u8 = static_cast<uint8_t>(event.a.get_tag());
50 const uint8_t tag_b_u8 = static_cast<uint8_t>(event.b.get_tag());
51
52 FF tag_a_inv = precomputed_inverses[tag_a_u8];
53 // For tag_a != tag_b
54 FF tag_ab_diff_inv = 0;
55 if (tag_a_u8 > tag_b_u8) {
56 tag_ab_diff_inv = precomputed_inverses[tag_a_u8 - tag_b_u8];
57 } else {
58 // (-x)^(-1) = -x^(-1) for a field element x.
59 tag_ab_diff_inv = -precomputed_inverses[tag_b_u8 - tag_a_u8];
60 }
61
62 if (is_tag_ff || is_tag_mismatch) {
63 // There is an error, fill in values that are still needed to satisfy constraints despite the error.
64 trace.set(row,
65 { {
66 { C::bitwise_op_id, static_cast<uint8_t>(event.operation) },
67 { C::bitwise_start, 1 },
68 { C::bitwise_sel_get_ctr, 0 },
69 { C::bitwise_last, 1 }, // Error triggers a last
70 { C::bitwise_acc_ia, uint256_t::from_uint128(input_a) },
71 { C::bitwise_acc_ib, uint256_t::from_uint128(input_b) },
72 { C::bitwise_acc_ic, uint256_t::from_uint128(output_c) },
73 { C::bitwise_ia_byte, uint256_t::from_uint128(input_a) },
74 { C::bitwise_ib_byte, uint256_t::from_uint128(input_b) },
75 { C::bitwise_ic_byte, uint256_t::from_uint128(output_c) },
76 { C::bitwise_tag_a, tag_a_u8 },
77 { C::bitwise_tag_b, tag_b_u8 },
78 { C::bitwise_tag_c, tag_a_u8 }, // same as tag_a
79 // Err Flags
80 { C::bitwise_sel_tag_ff_err, is_tag_ff ? 1 : 0 },
81 { C::bitwise_sel_tag_mismatch_err, is_tag_mismatch ? 1 : 0 },
82 { C::bitwise_err, 1 },
83 // Err Helpers
84 { C::bitwise_tag_a_inv, tag_a_inv },
85 { C::bitwise_tag_ab_diff_inv, tag_ab_diff_inv },
86
87 } });
88 row++;
89 continue; // Skip the rest of the processing for this event
90 }
91
92 // At this point we know that we will not error, so we can proceed with the bitwise operation.
93
94 // Note that for tag U1, we take only one bit. This is correctly
95 // captured below since input_a/b and output_c are each a single bit
96 // and the byte mask correctly extracts it.
97 const uint128_t mask_low_byte = (1 << 8) - 1;
98 const auto start_ctr = get_tag_bytes(tag);
99
100 for (int ctr = start_ctr; ctr > 0; ctr--) {
101 bool is_start = (ctr == start_ctr);
102 trace.set(row,
103 { { { C::bitwise_op_id, static_cast<uint8_t>(event.operation) },
104 { C::bitwise_acc_ia, uint256_t::from_uint128(input_a) },
105 { C::bitwise_acc_ib, uint256_t::from_uint128(input_b) },
106 { C::bitwise_acc_ic, uint256_t::from_uint128(output_c) },
107 { C::bitwise_ia_byte, uint256_t::from_uint128(input_a & mask_low_byte) },
108 { C::bitwise_ib_byte, uint256_t::from_uint128(input_b & mask_low_byte) },
109 { C::bitwise_ic_byte, uint256_t::from_uint128(output_c & mask_low_byte) },
110 { C::bitwise_tag_a, is_start ? tag_a_u8 : 0 },
111 { C::bitwise_tag_b, is_start ? tag_b_u8 : 0 },
112 { C::bitwise_tag_c, is_start ? tag_a_u8 : 0 }, // same as tag_a
113 { C::bitwise_ctr, ctr },
114 { C::bitwise_ctr_inv, precomputed_inverses[static_cast<uint8_t>(ctr)] },
115 { C::bitwise_ctr_min_one_inv, precomputed_inverses[static_cast<uint8_t>(ctr - 1)] },
116 { C::bitwise_last, ctr == 1 ? 1 : 0 },
117 { C::bitwise_sel, ctr != 0 ? 1 : 0 },
118 { C::bitwise_start, is_start ? 1 : 0 },
119 { C::bitwise_sel_get_ctr, is_start ? 1 : 0 }, // Same as bitwise_start but in non-error case
120 // Err Helpers, in the happy path we still need to prove we would not have errored
121 { C::bitwise_tag_a_inv, is_start ? tag_a_inv : 0 },
122 { C::bitwise_tag_ab_diff_inv, is_start ? tag_ab_diff_inv : 0 } } });
123
124 input_a >>= 8;
125 input_b >>= 8;
126 output_c >>= 8;
127 row++;
128 }
129 }
130}
131
135 .add<lookup_bitwise_integral_tag_length_settings, InteractionType::LookupIntoIndexedByClk>();
136
137} // namespace bb::avm2::tracegen
void process(const simulation::EventEmitterInterface< simulation::BitwiseEvent >::Container &events, TraceContainer &trace)
static const InteractionDefinition interactions
InteractionDefinition & add(auto &&... args)
static constexpr uint256_t from_uint128(const uint128_t a) noexcept
Definition uint256.hpp:94
TestTraceContainer trace
lookup_settings< lookup_bitwise_byte_operations_settings_ > lookup_bitwise_byte_operations_settings
AvmFlavorSettings::FF FF
Definition field.hpp:10
uint8_t get_tag_bytes(ValueTag tag)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
simulation::PublicDataTreeReadWriteEvent event
unsigned __int128 uint128_t
Definition serialize.hpp:44
static void batch_invert(C &coeffs) noexcept