-
Notifications
You must be signed in to change notification settings - Fork 595
Expand file tree
/
Copy pathcontract_instance_retrieval.pil
More file actions
303 lines (269 loc) · 13.5 KB
/
contract_instance_retrieval.pil
File metadata and controls
303 lines (269 loc) · 13.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
include "../constants_gen.pil";
include "../ff_gt.pil";
include "../precomputed.pil";
include "../public_inputs.pil";
include "../trees/indexed_tree_check.pil";
include "address_derivation.pil";
include "update_check.pil";
/**
* Contract Instance Retrieval gadget.
* For use by execution (GetContractInstance opcode) and by bytecode retrieval.
* Proves the existence of a deployed contract instance for the provided address,
* along with its derivation from its parts (deployer, class id, init hash).
*
* GADGET LOGIC:
* The gadget first classifies the address as protocol or non-protocol, then follows
* one of two paths to determine existence and validate the instance.
*
* For all addresses:
* - Classify via range check: address in [1, MAX_PROTOCOL_CONTRACTS] => protocol contract.
* - Force all instance members to 0 if the contract does not exist.
* - If the contract exists, constrain address derivation from instance parts (salt, deployer,
* original class id, init hash, public keys). Salt and public keys are hinted.
*
* For protocol contracts:
* - Read derived address from the public inputs column at a computed index.
* - Existence is determined by whether the derived address is non-zero.
* - No nullifier check or update check is performed.
*
* For non-protocol contracts:
* - Check (non)existence of the deployment nullifier (the contract address itself) in the
* nullifier tree, siloed under the deployer protocol contract address.
* - If the contract exists, enforce that the current class id is up-to-date via update check.
*
* PRECONDITIONS:
* - The caller must constrain nullifier_tree_root and public_data_tree_root to the correct
* tree roots for the current context.
* - The contract database must be consistent with the nullifier tree: if a deployment nullifier
* exists for a non-protocol address, the contract instance must be present in the DB.
* - The contract database must be consistent with the protocol contracts table: a protocol
* contract slot has a derived address if and only if the instance is present in the DB.
*
* USAGE:
* This gadget is a lookup destination for get_contract_instance.pil and bc_retrieval.pil.
* The "salt" member is not needed in the lookup — it is hinted to the retrieval gadget.
*
* sel {
* // inputs
* caller.address,
* caller.nullifier_tree_root,
* caller.public_data_tree_root
* // outputs
* caller.exists,
* caller.deployer_addr, // situational - only if caller needs it
* caller.current_class_id,
* caller.init_hash, // situational - only if caller needs it
* caller.immutables_hash // situational - only if caller needs it
* } in contract_instance_retrieval.sel {
* contract_instance_retrieval.address,
* contract_instance_retrieval.nullifier_tree_root,
* contract_instance_retrieval.public_data_tree_root,
* contract_instance_retrieval.exists,
* contract_instance_retrieval.deployer_addr,
* contract_instance_retrieval.current_class_id,
* contract_instance_retrieval.init_hash,
* contract_instance_retrieval.immutables_hash
* };
*
* Situational columns (deployer_addr, init_hash, immutables_hash) can be omitted if the caller doesn't need
* them. When omitted, they are only hinted for address derivation. This is secure because
* incorrect values would break derivation of the given address.
*
* TRACE SHAPE:
* 1 row per retrieval.
*
* ERROR HANDLING:
* This gadget does not have error conditions. Errors (invalid enum, out-of-bounds writes)
* are handled by the calling opcode gadget (get_contract_instance.pil).
*
* INTERACTIONS:
* - ff_gt.pil: Determine if address is in protocol contract range. (#[CHECK_PROTOCOL_ADDRESS_RANGE])
* - public_inputs.pil: Read derived address for protocol contracts. (#[READ_DERIVED_ADDRESS_FROM_PUBLIC_INPUTS])
* - indexed_tree_check.pil: Check (non)existence of deployment nullifier. (#[DEPLOYMENT_NULLIFIER_READ])
* - address_derivation.pil: Constrain address derivation from instance parts. (#[ADDRESS_DERIVATION])
* - update_check.pil: Enforce current class id is up-to-date. (#[UPDATE_CHECK])
*/
namespace contract_instance_retrieval;
#[skippable_if]
sel = 0;
pol commit sel; // @boolean
sel * (1 - sel) = 0;
// ==== Inputs and Outputs ====
pol commit address; // Contract address.
pol commit exists; // @boolean — the contract instance exists (its address nullifier exists, or for protocol contracts, its derived address in PI is nonzero).
exists * (1 - exists) = 0;
// Address instance members.
// See barretenberg/cpp/src/barretenberg/vm2/common/aztec_types.hpp
pol commit salt; // HINTED!
pol commit deployer_addr;
pol commit current_class_id;
pol commit original_class_id; // HINTED!
pol commit init_hash;
pol commit immutables_hash;
// Current state — these should be looked up and constrained by the caller.
pol commit nullifier_tree_root;
pol commit public_data_tree_root;
// ==== Hinted and Internal Columns ====
// The address that is checked in address derivation.
// This comes from public inputs for protocol contracts,
// or is just assigned "address" (from input) for non-protocol contracts.
pol commit derived_address;
// Public keys (all hinted).
pol commit nullifier_key_x;
pol commit nullifier_key_y;
pol commit incoming_viewing_key_x;
pol commit incoming_viewing_key_y;
pol commit outgoing_viewing_key_x;
pol commit outgoing_viewing_key_y;
pol commit tagging_key_x;
pol commit tagging_key_y;
// ==== Determine if It Is a Protocol Contract (<= max) ====
// Lookup constant support: Can be removed when we support constants in lookups.
pol commit deployer_protocol_contract_address;
#[DEPLOYER_PROTOCOL_CONTRACT_ADDRESS_CONSTANT]
sel * (constants.CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS - deployer_protocol_contract_address) = 0;
// Indicates if the instance belongs to a protocol contract.
pol commit is_protocol_contract; // @boolean (by lookup on active rows)
// Note that is_protocol_contract is not constrained to be boolean or nonzero on inactive rows.
// This should be fine as it is not used as the target selector for lookups into this trace
// and is not used to activate any interactions that create side effects (memory writes, public inputs writes).
// Worst-case, a malicious actor could prove an extra properly constrained address-derivation which should be harmless.
// Canonical addresses can be in the range of 1 <= address <= MAX_PROTOCOL_CONTRACTS.
pol commit max_protocol_contracts;
max_protocol_contracts = sel * constants.MAX_PROTOCOL_CONTRACTS;
pol commit address_sub_one;
address_sub_one = sel * (address - 1);
// Checks whether address is in the protocol contract range by looking up a > b comparison.
// When sel = 1: is_protocol_contract = 1 iff MAX_PROTOCOL_CONTRACTS > (address - 1),
// i.e., address <= MAX_PROTOCOL_CONTRACTS.
#[CHECK_PROTOCOL_ADDRESS_RANGE]
sel { max_protocol_contracts, address_sub_one, is_protocol_contract }
in
ff_gt.sel_gt { ff_gt.a, ff_gt.b, ff_gt.result };
// ==== Constrain "exists" and "derived_address" via Public Inputs (Protocol Contracts) ====
// Compute the protocol contract derived address index in the PI column as:
// base_offset + contract_index = base_offset + protocol_address - 1.
// Only constrained when is_protocol_contract=1; free otherwise — should be secure because
// it is only used in the PI lookup which is also gated by is_protocol_contract.
pol commit derived_address_pi_index;
#[DERIVED_ADDRESS_PI_INDEX]
is_protocol_contract * (constants.AVM_PUBLIC_INPUTS_PROTOCOL_CONTRACTS_ROW_IDX + address_sub_one - derived_address_pi_index) = 0;
#[READ_DERIVED_ADDRESS_FROM_PUBLIC_INPUTS]
is_protocol_contract {
derived_address_pi_index,
derived_address
} in public_inputs.sel {
precomputed.idx,
public_inputs.cols[0]
};
// Only constrained when is_protocol_contract=1; free otherwise — should be secure because
// it is only used in the zero-check which is also gated by is_protocol_contract.
pol commit protocol_contract_derived_address_inv; // @zero-check
pol NOT_EXISTS = 1 - exists;
// Zero-check with error support: exists iff derived_address != 0.
// See https://github.com/AztecProtocol/aztec-packages/blob/next/barretenberg/cpp/pil/vm2/docs/recipes.md#with-error-support.
#[PROTOCOL_CONTRACT_EXISTS_CHECK]
is_protocol_contract * (derived_address * (NOT_EXISTS * (1 - protocol_contract_derived_address_inv) + protocol_contract_derived_address_inv) - 1 + NOT_EXISTS) = 0;
// ==== Constrain "exists" and "derived_address" via Nullifier Check (Non-protocol Contracts) ====
pol commit should_check_nullifier; // @boolean (by definition)
// Protocol contracts do not have an address nullifier in the nullifier tree.
should_check_nullifier = sel * (1 - is_protocol_contract);
// Lookup constant support: Can be removed when we support constants in lookups.
pol commit nullifier_tree_height;
#[NULLIFIER_TREE_HEIGHT_CONSTANT]
should_check_nullifier * (nullifier_tree_height - constants.NULLIFIER_TREE_HEIGHT) = 0;
// Lookup constant support: Can be removed when we support constants in lookups.
pol commit siloing_separator;
#[SILOING_SEPARATOR_CONSTANT]
should_check_nullifier * (siloing_separator - constants.DOM_SEP__SILOED_NULLIFIER) = 0;
pol commit nullifier_merkle_separator;
should_check_nullifier * (nullifier_merkle_separator - constants.DOM_SEP__NULLIFIER_MERKLE) = 0;
// Nullifier existence check (deployment nullifier read).
#[DEPLOYMENT_NULLIFIER_READ]
should_check_nullifier {
exists, // does the contract address nullifier exist?
address, // the deployment nullifier
nullifier_tree_root,
nullifier_tree_height,
nullifier_merkle_separator,
sel, // 1 (yes silo)
siloing_separator,
deployer_protocol_contract_address
} in indexed_tree_check.sel {
indexed_tree_check.exists,
indexed_tree_check.value,
indexed_tree_check.root,
indexed_tree_check.tree_height,
indexed_tree_check.merkle_hash_separator,
indexed_tree_check.sel_silo,
indexed_tree_check.siloing_separator,
indexed_tree_check.address
};
// For non-protocol contracts, derived address just comes from this gadget's `address` input,
// unlike protocol contracts where it is looked up from public inputs above.
#[UNCHANGED_ADDRESS_NON_PROTOCOL]
sel * (1 - is_protocol_contract) * (derived_address - address) = 0;
// ==== If Exists, Constrain Address Derivation ====
// Force members to 0 if the instance doesn't exist.
#[INSTANCE_MEMBER_SALT_IS_ZERO_IF_DNE]
sel * (1 - exists) * salt = 0; // technically not needed since salt is hinted, but good for consistency
#[INSTANCE_MEMBER_DEPLOYER_IS_ZERO_IF_DNE]
sel * (1 - exists) * deployer_addr = 0;
#[INSTANCE_MEMBER_CLASS_ID_IS_ZERO_IF_DNE]
sel * (1 - exists) * current_class_id = 0;
#[INSTANCE_MEMBER_ORIGINAL_CLASS_ID_IS_ZERO_IF_DNE]
sel * (1 - exists) * original_class_id = 0; // technically not needed since original_class_id is hinted, but good for consistency
#[INSTANCE_MEMBER_INIT_HASH_IS_ZERO_IF_DNE]
sel * (1 - exists) * init_hash = 0;
#[INSTANCE_MEMBER_IMMUTABLES_HASH_IS_ZERO_IF_DNE]
sel * (1 - exists) * immutables_hash = 0;
// Address derivation lookup (only if the nullifier exists or for protocol contract instances).
#[ADDRESS_DERIVATION]
exists {
derived_address,
salt, // hinted
deployer_addr,
original_class_id,
init_hash,
immutables_hash,
nullifier_key_x,
nullifier_key_y,
incoming_viewing_key_x,
incoming_viewing_key_y,
outgoing_viewing_key_x,
outgoing_viewing_key_y,
tagging_key_x,
tagging_key_y
} in address_derivation.sel {
address_derivation.address,
address_derivation.salt,
address_derivation.deployer_addr,
address_derivation.class_id,
address_derivation.init_hash,
address_derivation.immutables_hash,
address_derivation.nullifier_key_x,
address_derivation.nullifier_key_y,
address_derivation.incoming_viewing_key_x,
address_derivation.incoming_viewing_key_y,
address_derivation.outgoing_viewing_key_x,
address_derivation.outgoing_viewing_key_y,
address_derivation.tagging_key_x,
address_derivation.tagging_key_y
};
// ==== Constrain That "current" Class ID Truly Is Current (Non-protocol Contracts That Exist) ====
// Enforce that the class id provided is the _current_/active one. Only check when nullifier exists.
// If the address nullifier doesn't exist (which excludes protocol contract instances), there is no need to check.
pol commit should_check_for_update; // @boolean (by definition)
should_check_for_update = should_check_nullifier * exists;
#[UPDATE_CHECK]
should_check_for_update {
address,
current_class_id,
original_class_id,
public_data_tree_root
} in update_check.sel {
update_check.address,
update_check.current_class_id,
update_check.original_class_id,
update_check.public_data_tree_root
};