Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
public_data_tree_trace.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cstdint>
5#include <limits>
6#include <unordered_map>
7#include <vector>
8
22
23// This tracegen file will generate both the public data check trace and the public data squash trace from the same
24// events. These two traces are sorted in a different order, so we need to process them separately.
25namespace bb::avm2::tracegen {
26
28using simulation::PublicDataTreeReadWriteEvent;
29
30namespace {
31
32struct EventWithDiscard {
33 simulation::PublicDataTreeReadWriteEvent event;
34 bool discard;
35};
36
48void process_public_data_tree_check_trace(const std::vector<EventWithDiscard>& events_with_discard,
49 const std::unordered_map<FF, uint32_t>& first_write_per_slot,
50 const std::unordered_map<FF, FF>& last_value_per_slot,
51 TraceContainer& trace)
52{
53 using C = Column;
54
55 // This is a shifted trace, so we start at 1
56 uint32_t row = 1;
57
59
60 for (size_t i = 0; i < events_with_discard.size(); i++) {
61 bool end = i == events_with_discard.size() - 1;
62 const auto& event = events_with_discard[i].event;
63 const bool discard = events_with_discard[i].discard;
64
65 bool exists = event.low_leaf_preimage.leaf.slot == event.leaf_slot;
66 FF slot_low_leaf_slot_diff = event.leaf_slot - event.low_leaf_preimage.leaf.slot;
67
68 bool next_slot_is_nonzero = false;
69 FF next_slot = 0;
70 if (!exists) {
71 next_slot_is_nonzero = event.low_leaf_preimage.nextKey != 0;
72 next_slot = event.low_leaf_preimage.nextKey;
73 }
74
75 bool write = event.write_data.has_value();
76 // Note: the protocol_write and non_protocol_write are set by the MultiPermutationBuilder, but I also set them
77 // here for clarity/testing. Can remove, but would need to change the NegativeSetProtocolWrite test.
78 bool protocol_write = event.execution_id == std::numeric_limits<uint32_t>::max();
79 bool should_insert = !exists && write;
80 bool nondiscarded_write = write && !discard;
81 bool sel_write_to_public_inputs =
82 nondiscarded_write && first_write_per_slot.at(event.leaf_slot) == event.execution_id;
83 FF final_value = nondiscarded_write ? last_value_per_slot.at(event.leaf_slot) : 0;
84
85 FF intermediate_root = 0;
87 FF updated_low_leaf_hash = 0;
88 FF new_leaf_hash = 0;
89 AppendOnlyTreeSnapshot next_snapshot = event.prev_snapshot;
90
91 if (write) {
92 next_snapshot = event.write_data->next_snapshot;
93 intermediate_root = event.write_data->intermediate_root;
94 updated_low_leaf = event.write_data->updated_low_leaf_preimage;
95 updated_low_leaf_hash = event.write_data->updated_low_leaf_hash;
96 new_leaf_hash = event.write_data->new_leaf_hash;
97 }
98 uint32_t clk = event.execution_id;
99 uint32_t clk_diff = end ? 0 : events_with_discard[i + 1].event.execution_id - clk;
100
101 uint32_t public_data_writes_length = write_idx -
103 static_cast<uint32_t>(sel_write_to_public_inputs);
104
105 trace.set(row,
106 { {
107 { C::public_data_check_sel, 1 },
108 { C::public_data_check_not_end, !end },
109 { C::public_data_check_end, end },
110 { C::public_data_check_value, event.value },
111 { C::public_data_check_slot, event.slot },
112 { C::public_data_check_root, event.prev_snapshot.root },
113 { C::public_data_check_address, event.contract_address },
114 { C::public_data_check_write_root, next_snapshot.root },
115 { C::public_data_check_tree_size_before_write, event.prev_snapshot.next_available_leaf_index },
116 { C::public_data_check_tree_size_after_write, next_snapshot.next_available_leaf_index },
117 { C::public_data_check_write, write },
118 { C::public_data_check_protocol_write, protocol_write },
119 { C::public_data_check_non_protocol_write, write && !protocol_write },
120 { C::public_data_check_clk, clk },
121 { C::public_data_check_discard, discard },
122 { C::public_data_check_low_leaf_slot, event.low_leaf_preimage.leaf.slot },
123 { C::public_data_check_low_leaf_value, event.low_leaf_preimage.leaf.value },
124 { C::public_data_check_low_leaf_next_index, event.low_leaf_preimage.nextIndex },
125 { C::public_data_check_low_leaf_next_slot, event.low_leaf_preimage.nextKey },
126 { C::public_data_check_updated_low_leaf_value, updated_low_leaf.leaf.value },
127 { C::public_data_check_updated_low_leaf_next_index, updated_low_leaf.nextIndex },
128 { C::public_data_check_updated_low_leaf_next_slot, updated_low_leaf.nextKey },
129 { C::public_data_check_low_leaf_index, event.low_leaf_index },
130 { C::public_data_check_clk_diff_lo, static_cast<uint16_t>(clk_diff) },
131 { C::public_data_check_clk_diff_hi, clk_diff >> 16 },
132 { C::public_data_check_leaf_slot, event.leaf_slot },
133 { C::public_data_check_siloing_separator, DOM_SEP__PUBLIC_LEAF_SLOT },
134 { C::public_data_check_leaf_not_exists, !exists },
135 { C::public_data_check_leaf_slot_low_leaf_slot_diff_inv,
136 slot_low_leaf_slot_diff }, // Will be inverted in batch later
137 { C::public_data_check_next_slot_is_nonzero, next_slot_is_nonzero },
138 { C::public_data_check_next_slot_inv, next_slot }, // Will be inverted in batch later
139 { C::public_data_check_low_leaf_hash, event.low_leaf_hash },
140 { C::public_data_check_intermediate_root, intermediate_root },
141 { C::public_data_check_tree_height, PUBLIC_DATA_TREE_HEIGHT },
142 { C::public_data_check_const_three, 3 },
143 { C::public_data_check_const_four, 4 },
144 { C::public_data_check_merkle_hash_separator, DOM_SEP__PUBLIC_DATA_MERKLE },
145 { C::public_data_check_updated_low_leaf_hash, updated_low_leaf_hash },
146 { C::public_data_check_should_insert, should_insert },
147 { C::public_data_check_new_leaf_hash, new_leaf_hash },
148 { C::public_data_check_write_idx, write_idx },
149 { C::public_data_check_non_discarded_write, nondiscarded_write },
150 { C::public_data_check_sel_write_to_public_inputs, sel_write_to_public_inputs },
151 { C::public_data_check_final_value, final_value },
152 { C::public_data_check_public_data_writes_length, public_data_writes_length },
153 { C::public_data_check_length_pi_idx,
155 } });
156 row++;
157 if (sel_write_to_public_inputs) {
158 write_idx++;
159 }
160 }
161}
162
174void process_squashing_trace(const std::vector<PublicDataTreeReadWriteEvent>& nondiscarded_writes,
175 const std::unordered_map<FF, uint32_t>& first_write_per_slot,
176 const std::unordered_map<FF, FF>& last_value_per_slot,
177 TraceContainer& trace)
178{
179 using C = Column;
180
181 // This is a shifted trace, so we start at 1
182 uint32_t row = 1;
183
184 for (size_t i = 0; i < nondiscarded_writes.size(); i++) {
185 bool end = i == nondiscarded_writes.size() - 1;
186 const auto& event = nondiscarded_writes[i];
187
188 uint32_t clk = event.execution_id;
189
190 bool leaf_slot_increase = false;
191 bool check_clock = false;
192 uint32_t clk_diff = 0;
193
194 if (!end) {
195 const auto& next_event = nondiscarded_writes[i + 1];
196
197 if (event.leaf_slot == next_event.leaf_slot) {
199 event.execution_id, next_event.execution_id, "Execution id is not less than next execution id");
200 // We check strictly increasing clk diff in the squash trace.
201 clk_diff = next_event.execution_id - event.execution_id - 1;
202 check_clock = true;
203 } else {
204 BB_ASSERT_LT(static_cast<uint256_t>(event.leaf_slot),
205 static_cast<uint256_t>(next_event.leaf_slot),
206 "Leaf slot is not less than next leaf slot");
207 leaf_slot_increase = true;
208 }
209 }
210
211 bool sel_write_to_public_inputs = first_write_per_slot.at(event.leaf_slot) == event.execution_id;
212 FF final_value = last_value_per_slot.at(event.leaf_slot);
213
214 trace.set(row,
215 { {
216 { C::public_data_squash_sel, 1 },
217 { C::public_data_squash_leaf_slot, event.leaf_slot },
218 { C::public_data_squash_value, event.value },
219 { C::public_data_squash_clk, clk },
220 { C::public_data_squash_write_to_public_inputs, sel_write_to_public_inputs },
221 { C::public_data_squash_leaf_slot_increase, leaf_slot_increase },
222 { C::public_data_squash_check_clock, check_clock },
223 { C::public_data_squash_clk_diff_lo, static_cast<uint16_t>(clk_diff) },
224 { C::public_data_squash_clk_diff_hi, clk_diff >> 16 },
225 { C::public_data_squash_final_value, final_value },
226 } });
227 row++;
228 }
229}
230
231} // namespace
232
244 TraceContainer& trace)
245{
246 std::vector<EventWithDiscard> events_with_discard;
247 std::unordered_map<FF, uint32_t> first_write_clk_per_slot;
248 std::unordered_map<FF, FF> last_write_value_per_slot;
249
250 events_with_discard.reserve(events.size());
251 // Initial order of events will be chronological order.
253 events_with_discard.push_back({ event, discard });
254 if (!discard && event.write_data.has_value()) {
255 if (!first_write_clk_per_slot.contains(event.leaf_slot)) {
256 first_write_clk_per_slot[event.leaf_slot] = event.execution_id;
257 }
258 last_write_value_per_slot[event.leaf_slot] = event.value;
259 }
260 });
261
262 // Sort by clk in ascending order (reads will come with clk=0)
263 std::ranges::sort(events_with_discard.begin(),
264 events_with_discard.end(),
265 [](const EventWithDiscard& a, const EventWithDiscard& b) {
266 return a.event.execution_id < b.event.execution_id;
267 });
268
269 process_public_data_tree_check_trace(
270 events_with_discard, first_write_clk_per_slot, last_write_value_per_slot, trace);
271
273 nondiscarded_writes.reserve(events_with_discard.size());
274 // Use only nondiscarded writes
275 for (const auto& event_with_discard : events_with_discard) {
276 if (event_with_discard.event.write_data.has_value() && !event_with_discard.discard) {
277 nondiscarded_writes.push_back(event_with_discard.event);
278 }
279 }
280
281 // Sort by slot, and then by clk
282 std::ranges::sort(nondiscarded_writes.begin(),
283 nondiscarded_writes.end(),
284 [](const PublicDataTreeReadWriteEvent& a, const PublicDataTreeReadWriteEvent& b) {
285 if (a.leaf_slot == b.leaf_slot) {
286 return a.execution_id < b.execution_id;
287 }
288 return static_cast<uint256_t>(a.leaf_slot) < static_cast<uint256_t>(b.leaf_slot);
289 });
290
291 process_squashing_trace(nondiscarded_writes, first_write_clk_per_slot, last_write_value_per_slot, trace);
292
293 // Batch invert the columns.
295 { { Column::public_data_check_leaf_slot_low_leaf_slot_diff_inv, Column::public_data_check_next_slot_inv } });
296}
297
298// This is a sorted trace, so we can't use sequential lookups. Instead, we'll use generic lookups and clk indexed ones
299// for the ones targeting precomputed or public inputs.
300const InteractionDefinition PublicDataTreeTraceBuilder::interactions =
301 InteractionDefinition()
302 // Public data read/write
303 .add<InteractionType::LookupGeneric, lookup_public_data_check_silo_poseidon2_settings>()
304 .add<InteractionType::LookupGeneric, lookup_public_data_check_low_leaf_slot_validation_settings>()
306 .add<InteractionType::LookupGeneric, lookup_public_data_check_low_leaf_poseidon2_0_settings>()
307 .add<InteractionType::LookupGeneric, lookup_public_data_check_low_leaf_poseidon2_1_settings>()
308 .add<InteractionType::LookupGeneric, lookup_public_data_check_updated_low_leaf_poseidon2_0_settings>()
310 .add<InteractionType::LookupGeneric, lookup_public_data_check_low_leaf_merkle_check_settings>()
311 .add<InteractionType::LookupGeneric, lookup_public_data_check_new_leaf_poseidon2_0_settings>()
312 .add<InteractionType::LookupGeneric, lookup_public_data_check_new_leaf_poseidon2_1_settings>()
313 .add<InteractionType::LookupGeneric, lookup_public_data_check_new_leaf_merkle_check_settings>()
314 .add<InteractionType::MultiPermutation, perm_sstore_storage_write_settings, perm_tx_balance_update_settings>(
315 Column::public_data_check_write)
316 .add<InteractionType::Permutation, perm_public_data_check_squashing_settings>()
317 .add<InteractionType::LookupIntoIndexedByRow,
319 .add<InteractionType::LookupIntoIndexedByRow, lookup_public_data_check_clk_diff_range_lo_settings>()
320 .add<InteractionType::LookupIntoIndexedByRow, lookup_public_data_check_clk_diff_range_hi_settings>()
321 .add<InteractionType::LookupIntoIndexedByRow,
323 // Public data squash
324 .add<InteractionType::LookupGeneric, lookup_public_data_squash_leaf_slot_increase_ff_gt_settings>()
325 .add<InteractionType::LookupIntoIndexedByRow, lookup_public_data_squash_clk_diff_range_lo_settings>()
326 .add<InteractionType::LookupIntoIndexedByRow, lookup_public_data_squash_clk_diff_range_hi_settings>();
327
328} // namespace bb::avm2::tracegen
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:143
#define AVM_PUBLIC_INPUTS_AVM_ACCUMULATED_DATA_ARRAY_LENGTHS_PUBLIC_DATA_WRITES_ROW_IDX
#define DOM_SEP__PUBLIC_LEAF_SLOT
#define DOM_SEP__PUBLIC_DATA_MERKLE
#define AVM_PUBLIC_INPUTS_AVM_ACCUMULATED_DATA_PUBLIC_DATA_WRITES_ROW_IDX
#define PUBLIC_DATA_TREE_HEIGHT
InteractionDefinition & add(auto &&... args)
void process(const simulation::EventEmitterInterface< simulation::PublicDataTreeCheckEvent >::Container &events, TraceContainer &trace)
Process the public data tree check events and populate the relevant columns in the trace.
void invert_columns(std::span< const Column > cols)
void set(Column col, uint32_t row, const FF &value)
TestTraceContainer trace
FF a
FF b
IndexedLeaf< PublicDataLeafValue > PublicDataTreeLeafPreimage
void process_with_discard(const std::vector< std::variant< EventType, simulation::CheckPointEventType > > &events, ProcessEventFn &&process_event)
Processes events from a stream, attaching a discard flag to indicate reverted checkpoints.
permutation_settings< perm_public_data_check_squashing_settings_ > perm_public_data_check_squashing_settings
lookup_settings< lookup_public_data_check_clk_diff_range_lo_settings_ > lookup_public_data_check_clk_diff_range_lo_settings
lookup_settings< lookup_public_data_check_low_leaf_next_slot_validation_settings_ > lookup_public_data_check_low_leaf_next_slot_validation_settings
lookup_settings< lookup_public_data_check_new_leaf_merkle_check_settings_ > lookup_public_data_check_new_leaf_merkle_check_settings
lookup_settings< lookup_public_data_squash_clk_diff_range_lo_settings_ > lookup_public_data_squash_clk_diff_range_lo_settings
lookup_settings< lookup_public_data_check_updated_low_leaf_poseidon2_1_settings_ > lookup_public_data_check_updated_low_leaf_poseidon2_1_settings
lookup_settings< lookup_public_data_check_silo_poseidon2_settings_ > lookup_public_data_check_silo_poseidon2_settings
lookup_settings< lookup_public_data_check_write_writes_length_to_public_inputs_settings_ > lookup_public_data_check_write_writes_length_to_public_inputs_settings
lookup_settings< lookup_public_data_check_new_leaf_poseidon2_0_settings_ > lookup_public_data_check_new_leaf_poseidon2_0_settings
lookup_settings< lookup_public_data_check_write_public_data_to_public_inputs_settings_ > lookup_public_data_check_write_public_data_to_public_inputs_settings
lookup_settings< lookup_public_data_check_low_leaf_poseidon2_1_settings_ > lookup_public_data_check_low_leaf_poseidon2_1_settings
void write(B &buf, field2< base_field, Params > const &value)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
simulation::PublicDataTreeReadWriteEvent event
static IndexedLeaf< PublicDataLeafValue > empty()