Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
concrete_dbs.cpp
Go to the documentation of this file.
2
7
8namespace bb::avm2::simulation {
9
10// Contracts DB starts.
12{
13 // Get the contract instance from the raw DB.
15 // If we didn't get a contract instance, we don't prove anything.
16 // It is the responsibility of the caller to prove what the protocol expects.
17 if (!instance.has_value()) {
18 return std::nullopt;
19 }
20 // If we did get a contract instance, we need to prove that the address is derived from the instance.
21 // For protocol contracts the input address is the canonical address, we need to retrieve the derived address.
22 AztecAddress derived_address;
24 // Extract the stored derived address from the protocol contract.
25 auto maybe_derived = get_derived_address(protocol_contracts, address);
26 BB_ASSERT(maybe_derived.has_value(),
27 "Derived address should be found for protocol contract whose instance is found");
28 derived_address = maybe_derived.value();
29 } else {
30 derived_address = address;
31 }
32
33 // Perform address derivation to verify the derived_address is correctly calculated from the instance.
34 // Emits AddressDerivationEvent (and corresponding Poseidon2HashEvents, Poseidon2PermutationEvents, ScalarMulEvent,
35 // and EccAddEvents) if we have yet to derive it. Otherwise, ignores the instance and simply returns.
36 address_derivation.assert_derivation(derived_address, instance.value());
37 return instance;
38}
39
41{
42 // Get the contract class from the raw DB.
44 if (!maybe_klass.has_value()) {
45 return std::nullopt;
46 }
47
48 // Get the bytecode commitment for this class.
49 std::optional<FF> maybe_bytecode_commitment = raw_contract_db.get_bytecode_commitment(class_id);
50 // If the class exists, the bytecode commitment must also exist.
51 BB_ASSERT(maybe_bytecode_commitment.has_value(), "Bytecode commitment not found");
52
53 // Perform class ID derivation to verify the class ID is correctly derived from the class data.
54 // Emits ClassIdDerivationEvent (and corresponding Poseidon2HashEvent and Poseidon2PermutationEvents) if
55 // we have yet to derive this class ID.
56 class_id_derivation.assert_derivation(maybe_klass->with_commitment(maybe_bytecode_commitment.value()));
57
58 return maybe_klass;
59}
60
65
71
72void ContractDB::add_contracts(const ContractDeploymentData& contract_deployment_data)
73{
74 raw_contract_db.add_contracts(contract_deployment_data);
75}
76
77// Merkle DB starts.
78
80{
81 // No event generated.
83 TreeCounters tree_counters = tree_counters_stack.top();
84 return {
85 .note_hash_tree = { .tree = tree_snapshots.note_hash_tree, .counter = tree_counters.note_hash_counter },
86 .nullifier_tree = { .tree = tree_snapshots.nullifier_tree, .counter = tree_counters.nullifier_counter },
87 .l1_to_l2_message_tree = { .tree = tree_snapshots.l1_to_l2_message_tree,
88 .counter = tree_counters.l2_to_l1_msg_counter },
89 .public_data_tree = { .tree = tree_snapshots.public_data_tree, .counter = written_public_data_slots.size() },
90 };
91}
92
93FF MerkleDB::storage_read(const AztecAddress& contract_address, const FF& slot) const
94{
95 auto [present, index] = raw_merkle_db.get_low_indexed_leaf(MerkleTreeId::PUBLIC_DATA_TREE,
96 unconstrained_compute_leaf_slot(contract_address, slot));
97 auto path = raw_merkle_db.get_sibling_path(MerkleTreeId::PUBLIC_DATA_TREE, index);
99
100 FF value = present ? preimage.leaf.value : 0;
101
103 slot, contract_address, value, preimage, index, path, raw_merkle_db.get_tree_roots().public_data_tree);
104
105 return value;
106}
107
108void MerkleDB::storage_write(const AztecAddress& contract_address,
109 const FF& slot,
110 const FF& value,
111 bool is_protocol_write)
112{
113 FF leaf_slot = unconstrained_compute_leaf_slot(contract_address, slot);
115
117
118 auto& low_leaf_hint = hint.low_leaf_witness_data.at(0);
119 auto& insertion_hint = hint.insertion_witness_data.at(0);
120
122 contract_address,
123 value,
124 low_leaf_hint.leaf,
125 low_leaf_hint.index,
126 low_leaf_hint.path,
127 snapshot_before,
128 insertion_hint.path,
129 is_protocol_write);
130
131 // This will throw an unexpected exception if it fails.
132 BB_ASSERT_EQ(snapshot_after, raw_merkle_db.get_tree_roots().public_data_tree, "Snapshot after mismatch");
133
134 if (!is_protocol_write) {
135 written_public_data_slots.insert(contract_address, slot);
136 }
137}
138
139bool MerkleDB::was_storage_written(const AztecAddress& contract_address, const FF& slot) const
140{
141 return written_public_data_slots.contains(contract_address, slot);
142}
143
144bool MerkleDB::nullifier_exists(const AztecAddress& contract_address, const FF& nullifier) const
145{
146 return nullifier_exists_internal(contract_address, nullifier);
147}
148
149bool MerkleDB::siloed_nullifier_exists(const FF& nullifier) const
150{
151 return nullifier_exists_internal(/*contract_address*/ std::nullopt, nullifier);
152}
153
154bool MerkleDB::nullifier_exists_internal(std::optional<AztecAddress> contract_address, const FF& nullifier) const
155{
156 FF siloed_nullifier = nullifier;
158 if (contract_address.has_value()) {
159 // Unconstrained siloing to fetch the hint, since the hints are keyed by siloed data.
160 // The siloing will later be constrained in the nullifier tree check gadget.
161 siloed_nullifier = unconstrained_silo_nullifier(contract_address.value(), nullifier);
162 siloing_params = IndexedTreeSiloingParameters{
163 .address = contract_address.value(),
164 .siloing_separator = DOM_SEP__SILOED_NULLIFIER,
165 };
166 }
167
168 auto [present, low_leaf_index] = raw_merkle_db.get_low_indexed_leaf(MerkleTreeId::NULLIFIER_TREE, siloed_nullifier);
169 auto low_leaf_path = raw_merkle_db.get_sibling_path(MerkleTreeId::NULLIFIER_TREE, low_leaf_index);
170 auto low_leaf_preimage = raw_merkle_db.get_leaf_preimage_nullifier_tree(low_leaf_index);
171
172 indexed_tree_check.assert_read(nullifier,
173 siloing_params,
174 present,
176 .value = low_leaf_preimage.leaf.nullifier,
177 .next_value = low_leaf_preimage.nextKey,
178 .next_index = low_leaf_preimage.nextIndex,
179 },
180 low_leaf_index,
181 low_leaf_path,
182 raw_merkle_db.get_tree_roots().nullifier_tree);
183
184 return present;
185}
186
187void MerkleDB::nullifier_write(const AztecAddress& contract_address, const FF& nullifier)
188{
189 return nullifier_write_internal(contract_address, nullifier);
190}
191
193{
194 return nullifier_write_internal(/*contract_address*/ std::nullopt, nullifier);
195}
196
198{
199 uint32_t nullifier_counter = tree_counters_stack.top().nullifier_counter;
200 FF siloed_nullifier = nullifier;
202 if (contract_address.has_value()) {
203 // Unconstrained siloing to fetch the hint, since the hints are keyed by siloed data.
204 // The siloing will later be constrained in the nullifier tree check gadget.
205 siloed_nullifier = unconstrained_silo_nullifier(contract_address.value(), nullifier);
206 siloing_params = IndexedTreeSiloingParameters{
207 .address = contract_address.value(),
208 .siloing_separator = DOM_SEP__SILOED_NULLIFIER,
209 };
210 }
211
212 auto [present, low_leaf_index] = raw_merkle_db.get_low_indexed_leaf(MerkleTreeId::NULLIFIER_TREE, siloed_nullifier);
214
215 SiblingPath low_leaf_path;
216 IndexedLeaf<NullifierLeafValue> low_leaf_preimage;
218
219 if (present) {
220 low_leaf_path = raw_merkle_db.get_sibling_path(MerkleTreeId::NULLIFIER_TREE, low_leaf_index);
221 low_leaf_preimage = raw_merkle_db.get_leaf_preimage_nullifier_tree(low_leaf_index);
222 } else {
223 auto insertion_result = raw_merkle_db.insert_indexed_leaves_nullifier_tree(siloed_nullifier);
224
225 low_leaf_path = insertion_result.low_leaf_witness_data.at(0).path;
226 low_leaf_preimage = insertion_result.low_leaf_witness_data.at(0).leaf;
227 insertion_path = insertion_result.insertion_witness_data.at(0).path;
228 }
229
230 AppendOnlyTreeSnapshot snapshot_after =
231 indexed_tree_check.write(nullifier,
232 siloing_params,
235 .value = low_leaf_preimage.leaf.nullifier,
236 .next_value = low_leaf_preimage.nextKey,
237 .next_index = low_leaf_preimage.nextIndex,
238 },
239 low_leaf_index,
240 low_leaf_path,
241 snapshot_before,
242 insertion_path);
243
244 // This will throw an unexpected exception if it fails.
245 BB_ASSERT_EQ(snapshot_after, raw_merkle_db.get_tree_roots().nullifier_tree, "Snapshot after mismatch");
246
247 if (!present) {
248 tree_counters_stack.top().nullifier_counter++;
249 } else {
250 throw NullifierCollisionException(format("Nullifier ", nullifier, " already exists"));
251 }
252}
253
254bool MerkleDB::note_hash_exists(uint64_t leaf_index, const FF& unique_note_hash) const
255{
256 auto leaf_value = raw_merkle_db.get_leaf_value(MerkleTreeId::NOTE_HASH_TREE, leaf_index);
257 auto path = raw_merkle_db.get_sibling_path(MerkleTreeId::NOTE_HASH_TREE, leaf_index);
258 return note_hash_tree_check.note_hash_exists(
259 unique_note_hash, leaf_value, leaf_index, path, raw_merkle_db.get_tree_roots().note_hash_tree);
260}
261
262void MerkleDB::note_hash_write(const AztecAddress& contract_address, const FF& note_hash)
263{
264 uint32_t note_hash_counter = tree_counters_stack.top().note_hash_counter;
265
267 // Unconstrained siloing and uniqueness to fetch the hint, since the hints are keyed by the unique note hash.
268 // The siloing and uniqueness will later be constrained in the note hash tree check gadget.
269 FF siloed_note_hash = unconstrained_silo_note_hash(contract_address, note_hash);
270 FF unique_note_hash = unconstrained_make_unique_note_hash(
271 siloed_note_hash, note_hash_tree_check.get_first_nullifier(), note_hash_counter);
272
273 raw_merkle_db.append_leaves(MerkleTreeId::NOTE_HASH_TREE, std::vector<FF>{ unique_note_hash });
274 SiblingPath path =
275 raw_merkle_db.get_sibling_path(MerkleTreeId::NOTE_HASH_TREE, snapshot_before.next_available_leaf_index);
276 AppendOnlyTreeSnapshot snapshot_after =
277 note_hash_tree_check.append_note_hash(note_hash, contract_address, note_hash_counter, path, snapshot_before);
278
279 // This will throw an unexpected exception if it fails.
280 BB_ASSERT_EQ(snapshot_after, raw_merkle_db.get_tree_roots().note_hash_tree, "Snapshot after mismatch");
281
282 tree_counters_stack.top().note_hash_counter++;
283}
284
285void MerkleDB::siloed_note_hash_write(const FF& siloed_note_hash)
286{
287 uint32_t note_hash_counter = tree_counters_stack.top().note_hash_counter;
289 // Unconstrained siloing and uniqueness to fetch the hint, since the hints are keyed by the unique note hash.
290 // The siloing and uniqueness will later be constrained in the note hash tree check gadget.
291 FF unique_note_hash = unconstrained_make_unique_note_hash(
292 siloed_note_hash, note_hash_tree_check.get_first_nullifier(), note_hash_counter);
293
294 raw_merkle_db.append_leaves(MerkleTreeId::NOTE_HASH_TREE, std::vector<FF>{ unique_note_hash });
295 SiblingPath path =
296 raw_merkle_db.get_sibling_path(MerkleTreeId::NOTE_HASH_TREE, snapshot_before.next_available_leaf_index);
297 AppendOnlyTreeSnapshot snapshot_after =
298 note_hash_tree_check.append_siloed_note_hash(siloed_note_hash, note_hash_counter, path, snapshot_before);
299
300 // This will throw an unexpected exception if it fails.
301 BB_ASSERT_EQ(snapshot_after, raw_merkle_db.get_tree_roots().note_hash_tree, "Snapshot after mismatch");
302
303 tree_counters_stack.top().note_hash_counter++;
304}
305
306void MerkleDB::unique_note_hash_write(const FF& unique_note_hash)
307{
308 uint32_t note_hash_counter = tree_counters_stack.top().note_hash_counter;
310
311 raw_merkle_db.append_leaves(MerkleTreeId::NOTE_HASH_TREE, std::vector<FF>{ unique_note_hash });
312 SiblingPath path =
313 raw_merkle_db.get_sibling_path(MerkleTreeId::NOTE_HASH_TREE, snapshot_before.next_available_leaf_index);
314 AppendOnlyTreeSnapshot snapshot_after =
315 note_hash_tree_check.append_unique_note_hash(unique_note_hash, note_hash_counter, path, snapshot_before);
316
317 // This will throw an unexpected exception if it fails.
318 BB_ASSERT_EQ(snapshot_after, raw_merkle_db.get_tree_roots().note_hash_tree, "Snapshot after mismatch");
319
320 tree_counters_stack.top().note_hash_counter++;
321}
322
323bool MerkleDB::l1_to_l2_msg_exists(uint64_t leaf_index, const FF& msg_hash) const
324{
325 auto leaf_value = raw_merkle_db.get_leaf_value(MerkleTreeId::L1_TO_L2_MESSAGE_TREE, leaf_index);
326 auto path = raw_merkle_db.get_sibling_path(MerkleTreeId::L1_TO_L2_MESSAGE_TREE, leaf_index);
328 msg_hash, leaf_value, leaf_index, path, raw_merkle_db.get_tree_roots().l1_to_l2_message_tree);
329}
330
332{
333 // The public data tree is not padded.
334 raw_merkle_db.pad_tree(MerkleTreeId::NOTE_HASH_TREE,
335 MAX_NOTE_HASHES_PER_TX - tree_counters_stack.top().note_hash_counter);
336 raw_merkle_db.pad_tree(MerkleTreeId::NULLIFIER_TREE,
337 MAX_NULLIFIERS_PER_TX - tree_counters_stack.top().nullifier_counter);
338}
339
341{
345 for (auto& listener : checkpoint_listeners) {
346 listener->on_checkpoint_created();
347 }
348}
349
351{
354 TreeCounters current_counters = tree_counters_stack.top();
356 tree_counters_stack.top() = current_counters;
357 for (auto& listener : checkpoint_listeners) {
358 listener->on_checkpoint_committed();
359 }
360}
361
363{
367 for (auto& listener : checkpoint_listeners) {
368 listener->on_checkpoint_reverted();
369 }
370}
371
373{
375}
376
377} // namespace bb::avm2::simulation
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
std::shared_ptr< Napi::ThreadSafeFunction > instance
#define AVM_PUBLIC_INPUTS_AVM_ACCUMULATED_DATA_NULLIFIERS_ROW_IDX
#define DOM_SEP__SILOED_NULLIFIER
#define MAX_NOTE_HASHES_PER_TX
#define MAX_NULLIFIERS_PER_TX
std::optional< std::string > get_debug_function_name(const AztecAddress &address, const FunctionSelector &selector) const override
ContractDBInterface & raw_contract_db
std::optional< ContractInstance > get_contract_instance(const AztecAddress &address) const override
const ProtocolContracts & protocol_contracts
std::optional< ContractClass > get_contract_class(const ContractClassId &class_id) const override
void add_contracts(const ContractDeploymentData &contract_deployment_data) override
std::optional< FF > get_bytecode_commitment(const ContractClassId &class_id) const override
virtual void add_contracts(const ContractDeploymentData &contract_deployment_data)=0
virtual std::optional< FF > get_bytecode_commitment(const ContractClassId &class_id) const =0
virtual std::optional< std::string > get_debug_function_name(const AztecAddress &address, const FunctionSelector &selector) const =0
virtual std::optional< ContractInstance > get_contract_instance(const AztecAddress &address) const =0
virtual std::optional< ContractClass > get_contract_class(const ContractClassId &class_id) const =0
virtual bool exists(const FF &msg_hash, const FF &leaf_value, uint64_t leaf_index, std::span< const FF > sibling_path, const AppendOnlyTreeSnapshot &snapshot)=0
virtual IndexedLeaf< PublicDataLeafValue > get_leaf_preimage_public_data_tree(index_t leaf_index) const =0
virtual TreeSnapshots get_tree_roots() const =0
virtual void pad_tree(MerkleTreeId tree_id, size_t num_leaves)=0
virtual IndexedLeaf< NullifierLeafValue > get_leaf_preimage_nullifier_tree(index_t leaf_index) const =0
virtual SequentialInsertionResult< NullifierLeafValue > insert_indexed_leaves_nullifier_tree(const NullifierLeafValue &leaf_value)=0
virtual GetLowIndexedLeafResponse get_low_indexed_leaf(MerkleTreeId tree_id, const FF &value) const =0
virtual SiblingPath get_sibling_path(MerkleTreeId tree_id, index_t leaf_index) const =0
virtual uint32_t get_checkpoint_id() const =0
virtual SequentialInsertionResult< PublicDataLeafValue > insert_indexed_leaves_public_data_tree(const PublicDataLeafValue &leaf_value)=0
virtual void append_leaves(MerkleTreeId tree_id, std::span< const FF > leaves)=0
virtual FF get_leaf_value(MerkleTreeId tree_id, index_t leaf_index) const =0
WrittenPublicDataSlotsInterface & written_public_data_slots
void note_hash_write(const AztecAddress &contract_address, const FF &note_hash) override
std::vector< CheckpointNotifiable * > checkpoint_listeners
std::stack< TreeCounters > tree_counters_stack
bool nullifier_exists(const AztecAddress &contract_address, const FF &nullifier) const override
LowLevelMerkleDBInterface & raw_merkle_db
void unique_note_hash_write(const FF &note_hash) override
void nullifier_write_internal(std::optional< AztecAddress > contract_address, const FF &nullifier)
TreeStates get_tree_state() const override
bool was_storage_written(const AztecAddress &contract_address, const FF &slot) const override
uint32_t get_checkpoint_id() const override
bool note_hash_exists(uint64_t leaf_index, const FF &unique_note_hash) const override
void siloed_note_hash_write(const FF &note_hash) override
void siloed_nullifier_write(const FF &nullifier) override
FF storage_read(const AztecAddress &contract_address, const FF &slot) const override
bool siloed_nullifier_exists(const FF &nullifier) const override
PublicDataTreeCheckInterface & public_data_tree_check
void storage_write(const AztecAddress &contract_address, const FF &slot, const FF &value, bool is_protocol_write) override
bool nullifier_exists_internal(std::optional< AztecAddress > contract_address, const FF &nullifier) const
void nullifier_write(const AztecAddress &contract_address, const FF &nullifier) override
L1ToL2MessageTreeCheckInterface & l1_to_l2_msg_tree_check
bool l1_to_l2_msg_exists(uint64_t leaf_index, const FF &msg_hash) const override
virtual AppendOnlyTreeSnapshot write(const FF &slot, const AztecAddress &contract_address, const FF &value, const PublicDataTreeLeafPreimage &low_leaf_preimage, uint64_t low_leaf_index, std::span< const FF > low_leaf_sibling_path, const AppendOnlyTreeSnapshot &prev_snapshot, std::span< const FF > insertion_sibling_path, bool is_protocol_write)=0
virtual void assert_read(const FF &slot, const AztecAddress &contract_address, const FF &value, const PublicDataTreeLeafPreimage &low_leaf_preimage, uint64_t low_leaf_index, std::span< const FF > sibling_path, const AppendOnlyTreeSnapshot &snapshot)=0
virtual bool contains(const AztecAddress &contract_address, const FF &slot)=0
virtual void insert(const AztecAddress &contract_address, const FF &slot)=0
std::string format(Args... args)
Definition log.hpp:23
AVM range check gadget for witness generation.
::bb::crypto::merkle_tree::fr_sibling_path SiblingPath
Definition db.hpp:36
FF unconstrained_make_unique_note_hash(const FF &siloed_note_hash, const FF &first_nullifier, uint64_t note_hash_counter)
Definition merkle.cpp:45
::bb::crypto::merkle_tree::PublicDataLeafValue PublicDataLeafValue
Definition db.hpp:38
FF unconstrained_compute_leaf_slot(const AztecAddress &contract_address, const FF &slot)
Definition merkle.cpp:30
FF unconstrained_silo_note_hash(const AztecAddress &contract_address, const FF &note_hash)
Definition merkle.cpp:40
FF unconstrained_silo_nullifier(const AztecAddress &contract_address, const FF &nullifier)
Definition merkle.cpp:35
AvmFlavorSettings::FF FF
Definition field.hpp:10
FF ContractClassId
std::optional< AztecAddress > get_derived_address(const ProtocolContracts &protocol_contracts, const AztecAddress &canonical_address)
bool is_protocol_contract_address(const AztecAddress &address)
FF FunctionSelector
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
AppendOnlyTreeSnapshot public_data_tree
AppendOnlyTreeSnapshot l1_to_l2_message_tree
AppendOnlyTreeSnapshot nullifier_tree
AppendOnlyTreeSnapshot note_hash_tree