@@ -13,7 +13,8 @@ include "../scalar_mul.pil";
1313 * 2. partial_address = H(DOM_SEP__PARTIAL_ADDRESS, class_id, salted_init_hash)
1414 * 3. incoming_viewing_key_hash = H(DOM_SEP__SINGLE_PUBLIC_KEY_HASH, incoming_viewing_key_x, incoming_viewing_key_y)
1515 * 4. public_keys_hash = H(DOM_SEP__PUBLIC_KEYS_HASH,
16- * nullifier_key_hash, incoming_viewing_key_hash, outgoing_viewing_key_hash, tagging_key_hash)
16+ * nullifier_key_hash, incoming_viewing_key_hash, outgoing_viewing_key_hash, tagging_key_hash,
17+ * message_signing_key_hash, fallback_key_hash)
1718 * 5. preaddress = H(DOM_SEP__CONTRACT_ADDRESS_V2, public_keys_hash, partial_address)
1819 * 6. preaddress_public_key = preaddress * G1
1920 * 7. address = (preaddress_public_key + incoming_viewing_key).x
@@ -22,10 +23,10 @@ include "../scalar_mul.pil";
2223 * curve. See the 'Hash Computations', 'Elliptic Curve Operations', and 'INTERACTIONS' sections
2324 * for details on how we enforce each step. This process follows Noir's AztecAddress::compute().
2425 *
25- * Per AZIP-8: only `incoming_viewing_key` is exposed to the circuit as a Grumpkin point (since
26- * address derivation needs the curve point). The other three master public keys (nullifier,
27- * outgoing viewing, tagging) are exposed only as their hash digests under
28- * DOM_SEP__SINGLE_PUBLIC_KEY_HASH; the PXE computes those hashes off-circuit and the circuit
26+ * Only `incoming_viewing_key` is exposed to the circuit as a Grumpkin point (since
27+ * address derivation needs the curve point). The other five master public keys (nullifier,
28+ * outgoing viewing, tagging, message-signing, fallback ) are exposed only as their hash digests
29+ * under DOM_SEP__SINGLE_PUBLIC_KEY_HASH; the PXE computes those hashes off-circuit and the circuit
2930 * trusts them.
3031 *
3132 * PRECONDITIONS: The correctness of the preimage members is not constrained here and must be
@@ -43,14 +44,18 @@ include "../scalar_mul.pil";
4344 * nullifier_key_hash,
4445 * incoming_viewing_key_x, incoming_viewing_key_y,
4546 * outgoing_viewing_key_hash,
46- * tagging_key_hash
47+ * tagging_key_hash,
48+ * message_signing_key_hash,
49+ * fallback_key_hash
4750 * } in address_derivation.sel {
4851 * address_derivation.address, address_derivation.salt, address_derivation.deployer_addr,
4952 * address_derivation.class_id, address_derivation.init_hash,
5053 * address_derivation.nullifier_key_hash,
5154 * address_derivation.incoming_viewing_key_x, address_derivation.incoming_viewing_key_y,
5255 * address_derivation.outgoing_viewing_key_hash,
53- * address_derivation.tagging_key_hash
56+ * address_derivation.tagging_key_hash,
57+ * address_derivation.message_signing_key_hash,
58+ * address_derivation.fallback_key_hash
5459 * };
5560 *
5661 * TRACE SHAPE: 1 row per address derivation computation. Note that simulation deduplicates addresses
@@ -67,11 +72,11 @@ include "../scalar_mul.pil";
6772 * the retrieved contract instance and public keys (#[ADDRESS_DERIVATION]).
6873 *
6974 * This subtrace looks up:
70- * - poseidon2_hash.pil: To constrain five Poseidon2 hashes across a total of 7 lookups/rounds:
75+ * - poseidon2_hash.pil: To constrain five Poseidon2 hashes across a total of 8 lookups/rounds:
7176 * - salted_init_hash: #[SALTED_INITIALIZATION_HASH_POSEIDON2_0..1]
7277 * - partial_address: #[PARTIAL_ADDRESS_POSEIDON2]
7378 * - incoming_viewing_key_hash: #[IVPK_M_HASH_POSEIDON2]
74- * - public_keys_hash: #[PUBLIC_KEYS_HASH_POSEIDON2_0..1 ]
79+ * - public_keys_hash: #[PUBLIC_KEYS_HASH_POSEIDON2_0..2 ]
7580 * - preaddress: #[PREADDRESS_POSEIDON2]
7681 * - scalar_mul.pil: To constrain that preaddress_public_key = preaddress * G1 on Grumpkin: #[PREADDRESS_SCALAR_MUL]
7782 * - ecc.pil: To constrain that address = (preaddress_public_key + incoming_viewing_key).x on Grumpkin: #[ADDRESS_ECADD]
@@ -100,13 +105,15 @@ namespace address_derivation;
100105 pol commit immutables_hash;
101106
102107 // Public keys (see PublicKeys in barretenberg/cpp/src/barretenberg/vm2/common/aztec_types.hpp).
103- // Per AZIP-8, only incoming_viewing_key is held as a Grumpkin point; the other three keys
108+ // Only incoming_viewing_key is held as a Grumpkin point; the other five keys
104109 // are held as their (off-circuit) hashes.
105110 pol commit nullifier_key_hash;
106111 pol commit incoming_viewing_key_x;
107112 pol commit incoming_viewing_key_y;
108113 pol commit outgoing_viewing_key_hash;
109114 pol commit tagging_key_hash;
115+ pol commit message_signing_key_hash;
116+ pol commit fallback_key_hash;
110117
111118
112119 ///////////////////////////////
@@ -118,15 +125,20 @@ namespace address_derivation;
118125 // 2. partial_address = H(DOM_SEP__PARTIAL_ADDRESS, class_id, salted_init_hash)
119126 // 3. incoming_viewing_key_hash = H(DOM_SEP__SINGLE_PUBLIC_KEY_HASH, incoming_viewing_key_x, incoming_viewing_key_y)
120127 // 4. public_keys_hash = H(DOM_SEP__PUBLIC_KEYS_HASH,
121- // nullifier_key_hash, incoming_viewing_key_hash, outgoing_viewing_key_hash, tagging_key_hash)
128+ // nullifier_key_hash, incoming_viewing_key_hash, outgoing_viewing_key_hash,
129+ // tagging_key_hash, message_signing_key_hash, fallback_key_hash)
122130 // 5. preaddress = H(DOM_SEP__CONTRACT_ADDRESS_V2, public_keys_hash, partial_address)
123131 //
124132
125133 // Lookup constant support: Can be removed when we support constants in lookups.
134+ pol commit const_two;
135+ sel * (const_two - 2) = 0;
126136 pol commit const_three;
127137 sel * (const_three - 3) = 0;
128138 pol commit const_five;
129139 sel * (const_five - 5) = 0;
140+ pol commit const_seven;
141+ sel * (const_seven - 7) = 0;
130142 pol commit salted_init_hash_domain_separator;
131143 sel * (salted_init_hash_domain_separator - constants.DOM_SEP__SALTED_INITIALIZATION_HASH) = 0;
132144 pol commit partial_address_domain_separator;
@@ -184,23 +196,30 @@ namespace address_derivation;
184196 sel { single_public_key_hash_domain_separator, incoming_viewing_key_x, incoming_viewing_key_y, incoming_viewing_key_hash, const_three }
185197 in poseidon2_hash.start { poseidon2_hash.input_0, poseidon2_hash.input_1, poseidon2_hash.input_2, poseidon2_hash.output, poseidon2_hash.input_len };
186198
187- // 4. Computation of public keys hash from the four single-key hashes.
199+ // 4. Computation of public keys hash from the six single-key hashes.
188200 pol commit public_keys_hash;
189201
190- // We have 5 inputs (DOM_SEP + 4 hashes), hence 2 permutation rounds:
191- // public_keys_hash = H(DOM_SEP__PUBLIC_KEYS_HASH, npk_m_hash, incoming_viewing_key_hash, ovpk_m_hash, tpk_m_hash)
192- // Round 1 (start, input_len=5): (DOM_SEP__PUBLIC_KEYS_HASH, npk_m_hash, incoming_viewing_key_hash)
193- // Round 2 (end): (ovpk_m_hash, tpk_m_hash, 0)
202+ // We have 7 inputs (DOM_SEP + 6 hashes), hence 3 permutation rounds:
203+ // public_keys_hash = H(DOM_SEP__PUBLIC_KEYS_HASH, npk_m_hash, incoming_viewing_key_hash,
204+ // ovpk_m_hash, tpk_m_hash, mspk_m_hash, fbpk_m_hash)
205+ // Round 1 (start, input_len=7): (DOM_SEP__PUBLIC_KEYS_HASH, npk_m_hash, incoming_viewing_key_hash)
206+ // Round 2 (middle, num_perm_rounds_rem=2): (ovpk_m_hash, tpk_m_hash, mspk_m_hash)
207+ // Round 3 (end, num_perm_rounds_rem=1): (fbpk_m_hash, 0, 0)
194208
195- // Enforces the first poseidon round of public_keys_hash. Note that we must lookup poseidon2_hash.input_len == 5
209+ // Enforces the first poseidon round of public_keys_hash. Note that we must lookup poseidon2_hash.input_len == 7
196210 // here since it is constrained in the poseidon trace on the start row.
197211 #[PUBLIC_KEYS_HASH_POSEIDON2_0]
198- sel { public_keys_hash_domain_separator, nullifier_key_hash, incoming_viewing_key_hash, public_keys_hash, const_five }
212+ sel { public_keys_hash_domain_separator, nullifier_key_hash, incoming_viewing_key_hash, public_keys_hash, const_seven }
199213 in poseidon2_hash.start { poseidon2_hash.input_0, poseidon2_hash.input_1, poseidon2_hash.input_2, poseidon2_hash.output, poseidon2_hash.input_len };
200214
201- // Enforces the second and final round of public_keys_hash. Note that we must enforce the padded value is zero here .
215+ // Enforces the middle ( second) round of public_keys_hash via the multi-row recipe .
202216 #[PUBLIC_KEYS_HASH_POSEIDON2_1]
203- sel { outgoing_viewing_key_hash, tagging_key_hash, precomputed.zero, public_keys_hash }
217+ sel { outgoing_viewing_key_hash, tagging_key_hash, message_signing_key_hash, public_keys_hash, const_two }
218+ in poseidon2_hash.sel { poseidon2_hash.input_0, poseidon2_hash.input_1, poseidon2_hash.input_2, poseidon2_hash.output, poseidon2_hash.num_perm_rounds_rem };
219+
220+ // Enforces the third and final round of public_keys_hash. Note that we must enforce the padded values are zero here.
221+ #[PUBLIC_KEYS_HASH_POSEIDON2_2]
222+ sel { fallback_key_hash, precomputed.zero, precomputed.zero, public_keys_hash }
204223 in poseidon2_hash.end { poseidon2_hash.input_0, poseidon2_hash.input_1, poseidon2_hash.input_2, poseidon2_hash.output };
205224
206225 // 5. Computation of preaddress
0 commit comments