@@ -130,6 +130,14 @@ bool check_tag_integral(AvmMemoryTag tag)
130
130
}
131
131
}
132
132
133
+ bool isCanonical (FF contract_address)
134
+ {
135
+ // TODO: constrain this!
136
+ return contract_address == CANONICAL_AUTH_REGISTRY_ADDRESS || contract_address == DEPLOYER_CONTRACT_ADDRESS ||
137
+ contract_address == REGISTERER_CONTRACT_ADDRESS || contract_address == MULTI_CALL_ENTRYPOINT_ADDRESS ||
138
+ contract_address == FEE_JUICE_ADDRESS || contract_address == ROUTER_ADDRESS;
139
+ }
140
+
133
141
} // anonymous namespace
134
142
135
143
/* *************************************************************************************************
@@ -147,29 +155,53 @@ void AvmTraceBuilder::rollback_to_non_revertible_checkpoint()
147
155
148
156
std::vector<uint8_t > AvmTraceBuilder::get_bytecode (const FF contract_address, bool check_membership)
149
157
{
150
- // uint32_t clk = 0;
151
- // auto clk = static_cast<uint32_t>(main_trace.size()) + 1;
158
+ auto clk = static_cast <uint32_t >(main_trace.size ()) + 1 ;
152
159
153
160
// Find the bytecode based on contract address of the public call request
154
161
const AvmContractBytecode bytecode_hint =
155
162
*std::ranges::find_if (execution_hints.all_contract_bytecode , [contract_address](const auto & contract) {
156
163
return contract.contract_instance .address == contract_address;
157
164
});
158
- if (check_membership) {
159
- // NullifierReadTreeHint nullifier_read_hint = bytecode_hint.contract_instance.membership_hint;
160
- // // hinted nullifier should match the specified contract address
161
- // ASSERT(nullifier_read_hint.low_leaf_preimage.nullifier == contract_address);
162
- // bool is_member = merkle_tree_trace_builder.perform_nullifier_read(clk,
163
- // nullifier_read_hint.low_leaf_preimage,
164
- // nullifier_read_hint.low_leaf_index,
165
- // nullifier_read_hint.low_leaf_sibling_path);
166
- // // TODO(dbanks12): handle non-existent bytecode
167
- // // if the contract address nullifier is hinted as "exists", the membership check should agree
168
- // ASSERT(is_member);
165
+
166
+ bool exists = true ;
167
+ if (check_membership && !isCanonical (contract_address)) {
168
+ const auto contract_address_nullifier = AvmMerkleTreeTraceBuilder::unconstrained_silo_nullifier (
169
+ DEPLOYER_CONTRACT_ADDRESS, /* nullifier=*/ contract_address);
170
+ // nullifier read hint for the contract address
171
+ NullifierReadTreeHint nullifier_read_hint = bytecode_hint.contract_instance .membership_hint ;
172
+
173
+ // If the hinted preimage matches the contract address nullifier, the membership check will prove its existence,
174
+ // otherwise the membership check will prove that a low-leaf exists that skips the contract address nullifier.
175
+ exists = nullifier_read_hint.low_leaf_preimage .nullifier == contract_address_nullifier;
176
+ // perform the membership or non-membership check
177
+ bool is_member = merkle_tree_trace_builder.perform_nullifier_read (clk,
178
+ nullifier_read_hint.low_leaf_preimage ,
179
+ nullifier_read_hint.low_leaf_index ,
180
+ nullifier_read_hint.low_leaf_sibling_path );
181
+ // membership check must always pass
182
+ ASSERT (is_member);
183
+
184
+ if (exists) {
185
+ // This was a membership proof!
186
+ // Assert that the hint's exists flag matches. The flag isn't really necessary...
187
+ ASSERT (bytecode_hint.contract_instance .exists );
188
+ } else {
189
+ // This was a non-membership proof!
190
+ // Enforce that the tree access membership checked a low-leaf that skips the contract address nullifier.
191
+ // Show that the contract address nullifier meets the non membership conditions (sandwich or max)
192
+ ASSERT (contract_address_nullifier < nullifier_read_hint.low_leaf_preimage .nullifier &&
193
+ (nullifier_read_hint.low_leaf_preimage .next_nullifier == FF::zero () ||
194
+ contract_address_nullifier > nullifier_read_hint.low_leaf_preimage .next_nullifier ));
195
+ }
169
196
}
170
197
171
- vinfo (" Found bytecode for contract address: " , contract_address);
172
- return bytecode_hint.bytecode ;
198
+ if (exists) {
199
+ vinfo (" Found bytecode for contract address: " , contract_address);
200
+ return bytecode_hint.bytecode ;
201
+ }
202
+ // TODO(dbanks12): handle non-existent bytecode
203
+ vinfo (" Bytecode not found for contract address: " , contract_address);
204
+ throw std::runtime_error (" Bytecode not found" );
173
205
}
174
206
175
207
void AvmTraceBuilder::insert_private_state (const std::vector<FF>& siloed_nullifiers,
@@ -3181,23 +3213,66 @@ AvmError AvmTraceBuilder::op_get_contract_instance(
3181
3213
error = AvmError::CHECK_TAG_ERROR;
3182
3214
}
3183
3215
3184
- // Read the contract instance
3185
- ContractInstanceHint instance = execution_hints.contract_instance_hints .at (read_address.val );
3186
-
3187
- FF member_value;
3188
- switch (chosen_member) {
3189
- case ContractInstanceMember::DEPLOYER:
3190
- member_value = instance.deployer_addr ;
3191
- break ;
3192
- case ContractInstanceMember::CLASS_ID:
3193
- member_value = instance.contract_class_id ;
3194
- break ;
3195
- case ContractInstanceMember::INIT_HASH:
3196
- member_value = instance.initialisation_hash ;
3197
- break ;
3198
- default :
3199
- member_value = 0 ;
3200
- break ;
3216
+ FF member_value = 0 ;
3217
+ bool exists = false ;
3218
+
3219
+ if (is_ok (error)) {
3220
+ const auto contract_address = read_address.val ;
3221
+ const auto contract_address_nullifier = AvmMerkleTreeTraceBuilder::unconstrained_silo_nullifier (
3222
+ DEPLOYER_CONTRACT_ADDRESS, /* nullifier=*/ contract_address);
3223
+ // Read the contract instance hint
3224
+ ContractInstanceHint instance = execution_hints.contract_instance_hints .at (contract_address);
3225
+
3226
+ if (isCanonical (contract_address)) {
3227
+ // skip membership check for canonical contracts
3228
+ exists = true ;
3229
+ } else {
3230
+ // nullifier read hint for the contract address
3231
+ NullifierReadTreeHint nullifier_read_hint = instance.membership_hint ;
3232
+
3233
+ // If the hinted preimage matches the contract address nullifier, the membership check will prove its
3234
+ // existence, otherwise the membership check will prove that a low-leaf exists that skips the contract
3235
+ // address nullifier.
3236
+ exists = nullifier_read_hint.low_leaf_preimage .nullifier == contract_address_nullifier;
3237
+
3238
+ bool is_member =
3239
+ merkle_tree_trace_builder.perform_nullifier_read (clk,
3240
+ nullifier_read_hint.low_leaf_preimage ,
3241
+ nullifier_read_hint.low_leaf_index ,
3242
+ nullifier_read_hint.low_leaf_sibling_path );
3243
+ // membership check must always pass
3244
+ ASSERT (is_member);
3245
+
3246
+ if (exists) {
3247
+ // This was a membership proof!
3248
+ // Assert that the hint's exists flag matches. The flag isn't really necessary...
3249
+ ASSERT (instance.exists );
3250
+ } else {
3251
+ // This was a non-membership proof!
3252
+ // Enforce that the tree access membership checked a low-leaf that skips the contract address nullifier.
3253
+ // Show that the contract address nullifier meets the non membership conditions (sandwich or max)
3254
+ ASSERT (contract_address_nullifier < nullifier_read_hint.low_leaf_preimage .nullifier &&
3255
+ (nullifier_read_hint.low_leaf_preimage .next_nullifier == FF::zero () ||
3256
+ contract_address_nullifier > nullifier_read_hint.low_leaf_preimage .next_nullifier ));
3257
+ }
3258
+ }
3259
+
3260
+ if (exists) {
3261
+ switch (chosen_member) {
3262
+ case ContractInstanceMember::DEPLOYER:
3263
+ member_value = instance.deployer_addr ;
3264
+ break ;
3265
+ case ContractInstanceMember::CLASS_ID:
3266
+ member_value = instance.contract_class_id ;
3267
+ break ;
3268
+ case ContractInstanceMember::INIT_HASH:
3269
+ member_value = instance.initialisation_hash ;
3270
+ break ;
3271
+ default :
3272
+ member_value = 0 ;
3273
+ break ;
3274
+ }
3275
+ }
3201
3276
}
3202
3277
3203
3278
// TODO(8603): once instructions can have multiple different tags for writes, write dst as FF and exists as
@@ -3241,7 +3316,7 @@ AvmError AvmTraceBuilder::op_get_contract_instance(
3241
3316
// TODO(8603): once instructions can have multiple different tags for writes, remove this and do a
3242
3317
// constrained writes
3243
3318
write_to_memory (resolved_dst_offset, member_value, AvmMemoryTag::FF);
3244
- write_to_memory (resolved_exists_offset, FF (static_cast <uint32_t >(instance. exists )), AvmMemoryTag::U1);
3319
+ write_to_memory (resolved_exists_offset, FF (static_cast <uint32_t >(exists)), AvmMemoryTag::U1);
3245
3320
3246
3321
// TODO(dbanks12): compute contract address nullifier from instance preimage and perform membership check
3247
3322
0 commit comments