@@ -130,6 +130,13 @@ bool check_tag_integral(AvmMemoryTag tag)
130130 }
131131}
132132
133+ bool isCanonical (FF contract_address)
134+ {
135+ return contract_address == CANONICAL_AUTH_REGISTRY_ADDRESS || contract_address == DEPLOYER_CONTRACT_ADDRESS ||
136+ contract_address == REGISTERER_CONTRACT_ADDRESS || contract_address == MULTI_CALL_ENTRYPOINT_ADDRESS ||
137+ contract_address == FEE_JUICE_ADDRESS || contract_address == ROUTER_ADDRESS;
138+ }
139+
133140} // anonymous namespace
134141
135142/* *************************************************************************************************
@@ -147,29 +154,53 @@ void AvmTraceBuilder::rollback_to_non_revertible_checkpoint()
147154
148155std::vector<uint8_t > AvmTraceBuilder::get_bytecode (const FF contract_address, bool check_membership)
149156{
150- // uint32_t clk = 0;
151- // auto clk = static_cast<uint32_t>(main_trace.size()) + 1;
157+ auto clk = static_cast <uint32_t >(main_trace.size ()) + 1 ;
152158
153159 // Find the bytecode based on contract address of the public call request
154160 const AvmContractBytecode bytecode_hint =
155161 *std::ranges::find_if (execution_hints.all_contract_bytecode , [contract_address](const auto & contract) {
156162 return contract.contract_instance .address == contract_address;
157163 });
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);
164+
165+ bool exists = true ;
166+ if (check_membership && !isCanonical (contract_address)) {
167+ const auto contract_address_nullifier = AvmMerkleTreeTraceBuilder::unconstrained_silo_nullifier (
168+ DEPLOYER_CONTRACT_ADDRESS, /* nullifier=*/ contract_address);
169+ // nullifier read hint for the contract address
170+ NullifierReadTreeHint nullifier_read_hint = bytecode_hint.contract_instance .membership_hint ;
171+
172+ // If the hinted preimage matches the contract address nullifier, the membership check will prove its existence,
173+ // otherwise the membership check will prove that a low-leaf exists that skips the contract address nullifier.
174+ exists = nullifier_read_hint.low_leaf_preimage .nullifier == contract_address_nullifier;
175+ // perform the membership or non-membership check
176+ bool is_member = merkle_tree_trace_builder.perform_nullifier_read (clk,
177+ nullifier_read_hint.low_leaf_preimage ,
178+ nullifier_read_hint.low_leaf_index ,
179+ nullifier_read_hint.low_leaf_sibling_path );
180+ // membership check must always pass
181+ ASSERT (is_member);
182+
183+ if (exists) {
184+ // This was a membership proof!
185+ // Assert that the hint's exists flag matches. The flag isn't really necessary...
186+ ASSERT (bytecode_hint.contract_instance .exists );
187+ } else {
188+ // This was a non-membership proof!
189+ // Enforce that the tree access membership checked a low-leaf that skips the contract address nullifier.
190+ // Show that the contract address nullifier meets the non membership conditions (sandwich or max)
191+ ASSERT (contract_address_nullifier < nullifier_read_hint.low_leaf_preimage .nullifier &&
192+ (nullifier_read_hint.low_leaf_preimage .next_nullifier == FF::zero () ||
193+ contract_address_nullifier > nullifier_read_hint.low_leaf_preimage .next_nullifier ));
194+ }
169195 }
170196
171- vinfo (" Found bytecode for contract address: " , contract_address);
172- return bytecode_hint.bytecode ;
197+ if (exists) {
198+ vinfo (" Found bytecode for contract address: " , contract_address);
199+ return bytecode_hint.bytecode ;
200+ }
201+ // TODO(dbanks12): handle non-existent bytecode
202+ vinfo (" Bytecode not found for contract address: " , contract_address);
203+ throw std::runtime_error (" Bytecode not found" );
173204}
174205
175206void AvmTraceBuilder::insert_private_state (const std::vector<FF>& siloed_nullifiers,
@@ -3197,23 +3228,66 @@ AvmError AvmTraceBuilder::op_get_contract_instance(
31973228 error = AvmError::CHECK_TAG_ERROR;
31983229 }
31993230
3200- // Read the contract instance
3201- ContractInstanceHint instance = execution_hints.contract_instance_hints .at (read_address.val );
3202-
3203- FF member_value;
3204- switch (chosen_member) {
3205- case ContractInstanceMember::DEPLOYER:
3206- member_value = instance.deployer_addr ;
3207- break ;
3208- case ContractInstanceMember::CLASS_ID:
3209- member_value = instance.contract_class_id ;
3210- break ;
3211- case ContractInstanceMember::INIT_HASH:
3212- member_value = instance.initialisation_hash ;
3213- break ;
3214- default :
3215- member_value = 0 ;
3216- break ;
3231+ FF member_value = 0 ;
3232+ bool exists = false ;
3233+
3234+ if (is_ok (error)) {
3235+ const auto contract_address = read_address.val ;
3236+ const auto contract_address_nullifier = AvmMerkleTreeTraceBuilder::unconstrained_silo_nullifier (
3237+ DEPLOYER_CONTRACT_ADDRESS, /* nullifier=*/ contract_address);
3238+ // Read the contract instance hint
3239+ ContractInstanceHint instance = execution_hints.contract_instance_hints .at (contract_address);
3240+
3241+ if (isCanonical (contract_address)) {
3242+ // skip membership check for canonical contracts
3243+ exists = true ;
3244+ } else {
3245+ // nullifier read hint for the contract address
3246+ NullifierReadTreeHint nullifier_read_hint = instance.membership_hint ;
3247+
3248+ // If the hinted preimage matches the contract address nullifier, the membership check will prove its
3249+ // existence, otherwise the membership check will prove that a low-leaf exists that skips the contract
3250+ // address nullifier.
3251+ exists = nullifier_read_hint.low_leaf_preimage .nullifier == contract_address_nullifier;
3252+
3253+ bool is_member =
3254+ merkle_tree_trace_builder.perform_nullifier_read (clk,
3255+ nullifier_read_hint.low_leaf_preimage ,
3256+ nullifier_read_hint.low_leaf_index ,
3257+ nullifier_read_hint.low_leaf_sibling_path );
3258+ // membership check must always pass
3259+ ASSERT (is_member);
3260+ }
3261+
3262+ if (exists) {
3263+ // This was a membership proof!
3264+ // Assert that the hint's exists flag matches. The flag isn't really necessary...
3265+ ASSERT (instance.exists );
3266+
3267+ switch (chosen_member) {
3268+ case ContractInstanceMember::DEPLOYER:
3269+ member_value = instance.deployer_addr ;
3270+ break ;
3271+ case ContractInstanceMember::CLASS_ID:
3272+ member_value = instance.contract_class_id ;
3273+ break ;
3274+ case ContractInstanceMember::INIT_HASH:
3275+ member_value = instance.initialisation_hash ;
3276+ break ;
3277+ default :
3278+ member_value = 0 ;
3279+ break ;
3280+ }
3281+ } else {
3282+ // This was a non-membership proof!
3283+ // Enforce that the tree access membership checked a low-leaf that skips the contract address nullifier.
3284+ // Show that the contract address nullifier meets the non membership conditions (sandwich or max)
3285+ ASSERT (contract_address_nullifier < nullifier_read_hint.low_leaf_preimage .nullifier &&
3286+ (nullifier_read_hint.low_leaf_preimage .next_nullifier == FF::zero () ||
3287+ contract_address_nullifier > nullifier_read_hint.low_leaf_preimage .next_nullifier ));
3288+ // Instance DNE. Return 0 to user code.
3289+ member_value = 0 ;
3290+ }
32173291 }
32183292
32193293 // TODO(8603): once instructions can have multiple different tags for writes, write dst as FF and exists as
@@ -3257,7 +3331,7 @@ AvmError AvmTraceBuilder::op_get_contract_instance(
32573331 // TODO(8603): once instructions can have multiple different tags for writes, remove this and do a
32583332 // constrained writes
32593333 write_to_memory (resolved_dst_offset, member_value, AvmMemoryTag::FF);
3260- write_to_memory (resolved_exists_offset, FF (static_cast <uint32_t >(instance. exists )), AvmMemoryTag::U1);
3334+ write_to_memory (resolved_exists_offset, FF (static_cast <uint32_t >(exists)), AvmMemoryTag::U1);
32613335
32623336 // TODO(dbanks12): compute contract address nullifier from instance preimage and perform membership check
32633337
0 commit comments