1use aztec_core::abi::{AbiValue, FunctionSelector, FunctionType};
8use aztec_core::constants::protocol_contract_address;
9use aztec_core::error::Error;
10use aztec_core::hash::{
11 compute_auth_wit_message_hash, compute_inner_auth_wit_hash_from_action, ChainInfo,
12 MessageHashOrIntent,
13};
14use aztec_core::tx::{AuthWitness, ExecutionPayload, FunctionCall};
15use aztec_core::types::{AztecAddress, Fr};
16
17use crate::wallet::{
18 ExecuteUtilityOptions, ProfileOptions, SendOptions, SendResult, SimulateOptions,
19 TxProfileResult, TxSimulationResult, Wallet,
20};
21
22pub struct SetPublicAuthWitInteraction<'a, W> {
33 wallet: &'a W,
34 from: AztecAddress,
35 call: FunctionCall,
36}
37
38impl<'a, W: Wallet> SetPublicAuthWitInteraction<'a, W> {
39 pub async fn create(
44 wallet: &'a W,
45 from: AztecAddress,
46 message_hash_or_intent: MessageHashOrIntent,
47 authorized: bool,
48 ) -> Result<Self, Error> {
49 let chain_info = wallet.get_chain_info().await?;
50 let message_hash = compute_auth_wit_message_hash(&message_hash_or_intent, &chain_info);
51
52 let call = FunctionCall {
53 to: protocol_contract_address::auth_registry(),
54 selector: FunctionSelector::from_signature("set_authorized(Field,bool)"),
55 args: vec![AbiValue::Field(message_hash), AbiValue::Boolean(authorized)],
56 function_type: FunctionType::Public,
57 is_static: false,
58 hide_msg_sender: false,
59 };
60
61 Ok(Self { wallet, from, call })
62 }
63
64 pub fn request(&self) -> ExecutionPayload {
66 ExecutionPayload {
67 calls: vec![self.call.clone()],
68 ..Default::default()
69 }
70 }
71
72 pub async fn simulate(&self, mut opts: SimulateOptions) -> Result<TxSimulationResult, Error> {
74 opts.from = self.from;
75 self.wallet.simulate_tx(self.request(), opts).await
76 }
77
78 pub async fn send(&self, mut opts: SendOptions) -> Result<SendResult, Error> {
80 opts.from = self.from;
81 self.wallet.send_tx(self.request(), opts).await
82 }
83
84 pub async fn profile(&self, mut opts: ProfileOptions) -> Result<TxProfileResult, Error> {
86 opts.from = self.from;
87 self.wallet.profile_tx(self.request(), opts).await
88 }
89}
90
91#[derive(Clone, Debug, PartialEq, Eq)]
97pub struct AuthWitValidity {
98 pub is_valid_in_private: bool,
100 pub is_valid_in_public: bool,
102}
103
104pub async fn lookup_validity<W: Wallet>(
116 wallet: &W,
117 on_behalf_of: &AztecAddress,
118 intent: &MessageHashOrIntent,
119 witness: &AuthWitness,
120) -> Result<AuthWitValidity, Error> {
121 let chain_info = wallet.get_chain_info().await?;
122
123 let (inner_hash, consumer) = match intent {
125 MessageHashOrIntent::Intent { caller, call } => {
126 let inner = compute_inner_auth_wit_hash_from_action(caller, call);
127 (inner, call.to)
128 }
129 MessageHashOrIntent::InnerHash {
130 consumer,
131 inner_hash,
132 } => (*inner_hash, *consumer),
133 MessageHashOrIntent::Hash { hash } => {
134 let is_valid_in_public =
137 check_public_validity(wallet, on_behalf_of, hash, &chain_info).await;
138 return Ok(AuthWitValidity {
139 is_valid_in_private: false,
140 is_valid_in_public,
141 });
142 }
143 };
144
145 let is_valid_in_private =
147 check_private_validity(wallet, on_behalf_of, &consumer, &inner_hash, witness).await;
148
149 let message_hash = compute_auth_wit_message_hash(intent, &chain_info);
151 let is_valid_in_public =
152 check_public_validity(wallet, on_behalf_of, &message_hash, &chain_info).await;
153
154 Ok(AuthWitValidity {
155 is_valid_in_private,
156 is_valid_in_public,
157 })
158}
159
160async fn check_private_validity<W: Wallet>(
162 wallet: &W,
163 on_behalf_of: &AztecAddress,
164 consumer: &AztecAddress,
165 inner_hash: &Fr,
166 witness: &AuthWitness,
167) -> bool {
168 let call = FunctionCall {
169 to: *on_behalf_of,
170 selector: FunctionSelector::from_signature("lookup_validity((Field),Field)"),
171 args: vec![AbiValue::Field(consumer.0), AbiValue::Field(*inner_hash)],
172 function_type: FunctionType::Utility,
173 is_static: true,
174 hide_msg_sender: false,
175 };
176
177 let opts = ExecuteUtilityOptions {
178 scope: *on_behalf_of,
179 auth_witnesses: vec![witness.clone()],
180 };
181
182 match wallet.execute_utility(call, opts).await {
183 Ok(result) => parse_boolean_result(&result.result),
184 Err(_) => false,
185 }
186}
187
188async fn check_public_validity<W: Wallet>(
193 wallet: &W,
194 on_behalf_of: &AztecAddress,
195 message_hash: &Fr,
196 _chain_info: &ChainInfo,
197) -> bool {
198 use aztec_core::hash::poseidon2_hash_with_separator;
199
200 let base_slot = Fr::from(2u64);
203 const MAP_SLOT_SEP: u32 = 4_015_149_901;
205 let intermediate = poseidon2_hash_with_separator(&[base_slot, on_behalf_of.0], MAP_SLOT_SEP);
206 let storage_slot = poseidon2_hash_with_separator(&[intermediate, *message_hash], MAP_SLOT_SEP);
207
208 match wallet
209 .get_public_storage_at(&protocol_contract_address::auth_registry(), &storage_slot)
210 .await
211 {
212 Ok(value) => value != Fr::zero(),
213 Err(_) => false,
214 }
215}
216
217fn parse_boolean_result(value: &serde_json::Value) -> bool {
219 match value {
222 serde_json::Value::Bool(b) => *b,
223 serde_json::Value::Number(n) => n.as_u64() == Some(1),
224 serde_json::Value::String(s) => {
225 s != "0x0000000000000000000000000000000000000000000000000000000000000000"
227 && s != "0"
228 && s != "false"
229 }
230 serde_json::Value::Array(arr) => {
231 arr.first().map_or(false, parse_boolean_result)
233 }
234 _ => false,
235 }
236}
237
238#[cfg(test)]
239#[allow(clippy::expect_used)]
240mod tests {
241 use super::*;
242 use crate::wallet::MockWallet;
243
244 fn sample_chain_info() -> ChainInfo {
245 ChainInfo {
246 chain_id: Fr::from(31337u64),
247 version: Fr::from(1u64),
248 }
249 }
250
251 #[tokio::test]
252 async fn set_public_auth_wit_targets_auth_registry() {
253 let wallet = MockWallet::new(sample_chain_info());
254 let from = AztecAddress(Fr::from(1u64));
255
256 let interaction = SetPublicAuthWitInteraction::create(
257 &wallet,
258 from,
259 MessageHashOrIntent::Hash {
260 hash: Fr::from(42u64),
261 },
262 true,
263 )
264 .await
265 .expect("create interaction");
266
267 let payload = interaction.request();
268 assert_eq!(payload.calls.len(), 1);
269 assert_eq!(
270 payload.calls[0].to,
271 protocol_contract_address::auth_registry()
272 );
273 assert_eq!(payload.calls[0].function_type, FunctionType::Public);
274 }
275
276 #[tokio::test]
277 async fn set_public_auth_wit_enforces_from() {
278 let wallet = MockWallet::new(sample_chain_info());
279 let from = AztecAddress(Fr::from(1u64));
280
281 let interaction = SetPublicAuthWitInteraction::create(
282 &wallet,
283 from,
284 MessageHashOrIntent::Hash {
285 hash: Fr::from(42u64),
286 },
287 true,
288 )
289 .await
290 .expect("create interaction");
291
292 let opts = SimulateOptions::default();
294 let _result = interaction.simulate(opts).await.expect("simulate");
295 }
297
298 #[tokio::test]
299 async fn set_public_auth_wit_can_profile() {
300 let wallet = MockWallet::new(sample_chain_info());
301 let from = AztecAddress(Fr::from(1u64));
302
303 let interaction = SetPublicAuthWitInteraction::create(
304 &wallet,
305 from,
306 MessageHashOrIntent::Hash {
307 hash: Fr::from(42u64),
308 },
309 true,
310 )
311 .await
312 .expect("create interaction");
313
314 let _result = interaction
315 .profile(ProfileOptions::default())
316 .await
317 .expect("profile");
318 }
319
320 #[tokio::test]
321 async fn lookup_validity_with_hash_returns_false_private() {
322 let wallet = MockWallet::new(sample_chain_info());
323 let on_behalf_of = AztecAddress(Fr::from(1u64));
324 let intent = MessageHashOrIntent::Hash {
325 hash: Fr::from(42u64),
326 };
327 let witness = AuthWitness::default();
328
329 let validity = lookup_validity(&wallet, &on_behalf_of, &intent, &witness)
330 .await
331 .expect("lookup validity");
332
333 assert!(!validity.is_valid_in_private);
335 assert!(!validity.is_valid_in_public);
337 }
338
339 #[tokio::test]
340 async fn lookup_validity_with_intent() {
341 let wallet = MockWallet::new(sample_chain_info());
342 let on_behalf_of = AztecAddress(Fr::from(1u64));
343 let caller = AztecAddress(Fr::from(2u64));
344 let call = FunctionCall {
345 to: AztecAddress(Fr::from(3u64)),
346 selector: FunctionSelector::from_hex("0xaabbccdd").expect("valid"),
347 args: vec![AbiValue::Field(Fr::from(100u64))],
348 function_type: FunctionType::Private,
349 is_static: false,
350 hide_msg_sender: false,
351 };
352 let intent = MessageHashOrIntent::Intent { caller, call };
353 let witness = AuthWitness::default();
354
355 let validity = lookup_validity(&wallet, &on_behalf_of, &intent, &witness)
356 .await
357 .expect("lookup validity");
358
359 assert!(!validity.is_valid_in_private);
361 assert!(!validity.is_valid_in_public);
362 }
363
364 #[test]
365 fn parse_boolean_result_variants() {
366 assert!(parse_boolean_result(&serde_json::Value::Bool(true)));
367 assert!(!parse_boolean_result(&serde_json::Value::Bool(false)));
368 assert!(parse_boolean_result(&serde_json::json!(1)));
369 assert!(!parse_boolean_result(&serde_json::json!(0)));
370 assert!(!parse_boolean_result(&serde_json::Value::Null));
371 }
372}