tempo_precompiles/tip403_registry/
dispatch.rs1use crate::{
2 Precompile, fill_precompile_output, input_cost, mutate, mutate_void, unknown_selector, view,
3};
4use alloy::{primitives::Address, sol_types::SolCall};
5use revm::precompile::{PrecompileError, PrecompileResult};
6
7use crate::tip403_registry::{ITIP403Registry, TIP403Registry};
8
9impl Precompile for TIP403Registry {
10 fn call(&mut self, calldata: &[u8], msg_sender: Address) -> PrecompileResult {
11 self.storage
12 .deduct_gas(input_cost(calldata.len()))
13 .map_err(|_| PrecompileError::OutOfGas)?;
14
15 let selector: [u8; 4] = calldata
16 .get(..4)
17 .ok_or_else(|| {
18 PrecompileError::Other("Invalid input: missing function selector".into())
19 })?
20 .try_into()
21 .unwrap();
22
23 let result = match selector {
24 ITIP403Registry::policyIdCounterCall::SELECTOR => {
25 view::<ITIP403Registry::policyIdCounterCall>(calldata, |_call| {
26 self.policy_id_counter()
27 })
28 }
29 ITIP403Registry::policyDataCall::SELECTOR => {
30 view::<ITIP403Registry::policyDataCall>(calldata, |call| self.policy_data(call))
31 }
32 ITIP403Registry::isAuthorizedCall::SELECTOR => {
33 view::<ITIP403Registry::isAuthorizedCall>(calldata, |call| self.is_authorized(call))
34 }
35 ITIP403Registry::createPolicyCall::SELECTOR => {
36 mutate::<ITIP403Registry::createPolicyCall>(calldata, msg_sender, |s, call| {
37 self.create_policy(s, call)
38 })
39 }
40 ITIP403Registry::createPolicyWithAccountsCall::SELECTOR => {
41 mutate::<ITIP403Registry::createPolicyWithAccountsCall>(
42 calldata,
43 msg_sender,
44 |s, call| self.create_policy_with_accounts(s, call),
45 )
46 }
47 ITIP403Registry::setPolicyAdminCall::SELECTOR => {
48 mutate_void::<ITIP403Registry::setPolicyAdminCall>(
49 calldata,
50 msg_sender,
51 |s, call| self.set_policy_admin(s, call),
52 )
53 }
54 ITIP403Registry::modifyPolicyWhitelistCall::SELECTOR => {
55 mutate_void::<ITIP403Registry::modifyPolicyWhitelistCall>(
56 calldata,
57 msg_sender,
58 |s, call| self.modify_policy_whitelist(s, call),
59 )
60 }
61 ITIP403Registry::modifyPolicyBlacklistCall::SELECTOR => {
62 mutate_void::<ITIP403Registry::modifyPolicyBlacklistCall>(
63 calldata,
64 msg_sender,
65 |s, call| self.modify_policy_blacklist(s, call),
66 )
67 }
68 _ => unknown_selector(selector, self.storage.gas_used(), self.storage.spec()),
69 };
70
71 result.map(|res| fill_precompile_output(res, &mut self.storage))
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use crate::{
79 storage::{StorageCtx, hashmap::HashMapStorageProvider},
80 test_util::{assert_full_coverage, check_selector_coverage},
81 };
82 use alloy::sol_types::SolValue;
83 use tempo_chainspec::hardfork::TempoHardfork;
84 use tempo_contracts::precompiles::ITIP403Registry::ITIP403RegistryCalls;
85
86 #[test]
87 fn test_is_authorized_precompile() -> eyre::Result<()> {
88 let mut storage = HashMapStorageProvider::new(1);
89 let user = Address::random();
90 StorageCtx::enter(&mut storage, || {
91 let mut registry = TIP403Registry::new();
92
93 let call = ITIP403Registry::isAuthorizedCall { policyId: 1, user };
95 let calldata = call.abi_encode();
96 let result = registry.call(&calldata, Address::ZERO);
97
98 assert!(result.is_ok());
99 let output = result.unwrap();
100 let decoded: bool =
101 ITIP403Registry::isAuthorizedCall::abi_decode_returns(&output.bytes).unwrap();
102 assert!(decoded);
103
104 Ok(())
105 })
106 }
107
108 #[test]
109 fn test_create_policy_precompile() -> eyre::Result<()> {
110 let mut storage = HashMapStorageProvider::new(1);
111 let admin = Address::random();
112 StorageCtx::enter(&mut storage, || {
113 let mut registry = TIP403Registry::new();
114
115 let call = ITIP403Registry::createPolicyCall {
116 admin,
117 policyType: ITIP403Registry::PolicyType::WHITELIST,
118 };
119 let calldata = call.abi_encode();
120 let result = registry.call(&calldata, admin);
121
122 assert!(result.is_ok());
123 let output = result.unwrap();
124 let decoded: u64 =
125 ITIP403Registry::createPolicyCall::abi_decode_returns(&output.bytes).unwrap();
126 assert_eq!(decoded, 2); Ok(())
129 })
130 }
131
132 #[test]
133 fn test_policy_id_counter_initialization() -> eyre::Result<()> {
134 let mut storage = HashMapStorageProvider::new(1);
135 let sender = Address::random();
136 StorageCtx::enter(&mut storage, || {
137 let mut registry = TIP403Registry::new();
138
139 let counter_call = ITIP403Registry::policyIdCounterCall {};
141 let calldata = counter_call.abi_encode();
142 let result = registry.call(&calldata, sender).unwrap();
143 let counter = u64::abi_decode(&result.bytes).unwrap();
144 assert_eq!(counter, 2); Ok(())
147 })
148 }
149
150 #[test]
151 fn test_create_policy_with_accounts() -> eyre::Result<()> {
152 let mut storage = HashMapStorageProvider::new(1);
153 let admin = Address::random();
154 let account1 = Address::random();
155 let account2 = Address::random();
156 let other_account = Address::random();
157 StorageCtx::enter(&mut storage, || {
158 let mut registry = TIP403Registry::new();
159
160 let accounts = vec![account1, account2];
161 let call = ITIP403Registry::createPolicyWithAccountsCall {
162 admin,
163 policyType: ITIP403Registry::PolicyType::WHITELIST,
164 accounts,
165 };
166 let calldata = call.abi_encode();
167 let result = registry.call(&calldata, admin).unwrap();
168
169 let policy_id: u64 =
170 ITIP403Registry::createPolicyWithAccountsCall::abi_decode_returns(&result.bytes)
171 .unwrap();
172 assert_eq!(policy_id, 2);
173
174 let is_auth_call = ITIP403Registry::isAuthorizedCall {
176 policyId: policy_id,
177 user: account1,
178 };
179 let calldata = is_auth_call.abi_encode();
180 let result = registry.call(&calldata, admin).unwrap();
181 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
182 assert!(is_authorized);
183
184 let is_auth_call = ITIP403Registry::isAuthorizedCall {
185 policyId: policy_id,
186 user: account2,
187 };
188 let calldata = is_auth_call.abi_encode();
189 let result = registry.call(&calldata, admin).unwrap();
190 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
191 assert!(is_authorized);
192
193 let is_auth_call = ITIP403Registry::isAuthorizedCall {
195 policyId: policy_id,
196 user: other_account,
197 };
198 let calldata = is_auth_call.abi_encode();
199 let result = registry.call(&calldata, admin).unwrap();
200 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
201 assert!(!is_authorized);
202
203 Ok(())
204 })
205 }
206
207 #[test]
208 fn test_blacklist_policy() -> eyre::Result<()> {
209 let mut storage = HashMapStorageProvider::new(1);
210 let admin = Address::random();
211 let blocked_account = Address::random();
212 let allowed_account = Address::random();
213 StorageCtx::enter(&mut storage, || {
214 let mut registry = TIP403Registry::new();
215
216 let call = ITIP403Registry::createPolicyCall {
218 admin,
219 policyType: ITIP403Registry::PolicyType::BLACKLIST,
220 };
221 let calldata = call.abi_encode();
222 let result = registry.call(&calldata, admin).unwrap();
223 let policy_id: u64 =
224 ITIP403Registry::createPolicyCall::abi_decode_returns(&result.bytes).unwrap();
225
226 let is_auth_call = ITIP403Registry::isAuthorizedCall {
228 policyId: policy_id,
229 user: blocked_account,
230 };
231 let calldata = is_auth_call.abi_encode();
232 let result = registry.call(&calldata, admin).unwrap();
233 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
234 assert!(is_authorized);
235
236 let modify_call = ITIP403Registry::modifyPolicyBlacklistCall {
238 policyId: policy_id,
239 account: blocked_account,
240 restricted: true,
241 };
242 let calldata = modify_call.abi_encode();
243 registry.call(&calldata, admin).unwrap();
244
245 let is_auth_call = ITIP403Registry::isAuthorizedCall {
247 policyId: policy_id,
248 user: blocked_account,
249 };
250 let calldata = is_auth_call.abi_encode();
251 let result = registry.call(&calldata, admin).unwrap();
252 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
253 assert!(!is_authorized);
254
255 let is_auth_call = ITIP403Registry::isAuthorizedCall {
257 policyId: policy_id,
258 user: allowed_account,
259 };
260 let calldata = is_auth_call.abi_encode();
261 let result = registry.call(&calldata, admin).unwrap();
262 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
263 assert!(is_authorized);
264
265 let modify_call = ITIP403Registry::modifyPolicyBlacklistCall {
267 policyId: policy_id,
268 account: blocked_account,
269 restricted: false,
270 };
271 let calldata = modify_call.abi_encode();
272 registry.call(&calldata, admin).unwrap();
273
274 let is_auth_call = ITIP403Registry::isAuthorizedCall {
276 policyId: policy_id,
277 user: blocked_account,
278 };
279 let calldata = is_auth_call.abi_encode();
280 let result = registry.call(&calldata, admin).unwrap();
281 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
282 assert!(is_authorized);
283
284 Ok(())
285 })
286 }
287
288 #[test]
289 fn test_modify_policy_whitelist() -> eyre::Result<()> {
290 let mut storage = HashMapStorageProvider::new(1);
291 let admin = Address::random();
292 let account1 = Address::random();
293 let account2 = Address::random();
294 StorageCtx::enter(&mut storage, || {
295 let mut registry = TIP403Registry::new();
296
297 let call = ITIP403Registry::createPolicyCall {
299 admin,
300 policyType: ITIP403Registry::PolicyType::WHITELIST,
301 };
302 let calldata = call.abi_encode();
303 let result = registry.call(&calldata, admin).unwrap();
304 let policy_id: u64 =
305 ITIP403Registry::createPolicyCall::abi_decode_returns(&result.bytes).unwrap();
306
307 let modify_call1 = ITIP403Registry::modifyPolicyWhitelistCall {
309 policyId: policy_id,
310 account: account1,
311 allowed: true,
312 };
313 let calldata = modify_call1.abi_encode();
314 registry.call(&calldata, admin).unwrap();
315
316 let modify_call2 = ITIP403Registry::modifyPolicyWhitelistCall {
317 policyId: policy_id,
318 account: account2,
319 allowed: true,
320 };
321 let calldata = modify_call2.abi_encode();
322 registry.call(&calldata, admin).unwrap();
323
324 let is_auth_call = ITIP403Registry::isAuthorizedCall {
326 policyId: policy_id,
327 user: account1,
328 };
329 let calldata = is_auth_call.abi_encode();
330 let result = registry.call(&calldata, admin).unwrap();
331 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
332 assert!(is_authorized);
333
334 let is_auth_call = ITIP403Registry::isAuthorizedCall {
335 policyId: policy_id,
336 user: account2,
337 };
338 let calldata = is_auth_call.abi_encode();
339 let result = registry.call(&calldata, admin).unwrap();
340 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
341 assert!(is_authorized);
342
343 let modify_call = ITIP403Registry::modifyPolicyWhitelistCall {
345 policyId: policy_id,
346 account: account1,
347 allowed: false,
348 };
349 let calldata = modify_call.abi_encode();
350 registry.call(&calldata, admin).unwrap();
351
352 let is_auth_call = ITIP403Registry::isAuthorizedCall {
354 policyId: policy_id,
355 user: account1,
356 };
357 let calldata = is_auth_call.abi_encode();
358 let result = registry.call(&calldata, admin).unwrap();
359 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
360 assert!(!is_authorized);
361
362 let is_auth_call = ITIP403Registry::isAuthorizedCall {
363 policyId: policy_id,
364 user: account2,
365 };
366 let calldata = is_auth_call.abi_encode();
367 let result = registry.call(&calldata, admin).unwrap();
368 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
369 assert!(is_authorized);
370
371 Ok(())
372 })
373 }
374
375 #[test]
376 fn test_set_policy_admin() -> eyre::Result<()> {
377 let mut storage = HashMapStorageProvider::new(1);
378 let admin = Address::random();
379 let new_admin = Address::random();
380 StorageCtx::enter(&mut storage, || {
381 let mut registry = TIP403Registry::new();
382
383 let call = ITIP403Registry::createPolicyCall {
385 admin,
386 policyType: ITIP403Registry::PolicyType::WHITELIST,
387 };
388 let calldata = call.abi_encode();
389 let result = registry.call(&calldata, admin).unwrap();
390 let policy_id: u64 =
391 ITIP403Registry::createPolicyCall::abi_decode_returns(&result.bytes).unwrap();
392
393 let policy_data_call = ITIP403Registry::policyDataCall {
395 policyId: policy_id,
396 };
397 let calldata = policy_data_call.abi_encode();
398 let result = registry.call(&calldata, admin).unwrap();
399 let policy_data =
400 ITIP403Registry::policyDataCall::abi_decode_returns(&result.bytes).unwrap();
401 assert_eq!(policy_data.admin, admin);
402
403 let set_admin_call = ITIP403Registry::setPolicyAdminCall {
405 policyId: policy_id,
406 admin: new_admin,
407 };
408 let calldata = set_admin_call.abi_encode();
409 registry.call(&calldata, admin).unwrap();
410
411 let policy_data_call = ITIP403Registry::policyDataCall {
413 policyId: policy_id,
414 };
415 let calldata = policy_data_call.abi_encode();
416 let result = registry.call(&calldata, admin).unwrap();
417 let policy_data =
418 ITIP403Registry::policyDataCall::abi_decode_returns(&result.bytes).unwrap();
419 assert_eq!(policy_data.admin, new_admin);
420
421 Ok(())
422 })
423 }
424
425 #[test]
426 fn test_special_policy_ids() -> eyre::Result<()> {
427 let mut storage = HashMapStorageProvider::new(1);
428 let user = Address::random();
429 StorageCtx::enter(&mut storage, || {
430 let mut registry = TIP403Registry::new();
431
432 let is_auth_call = ITIP403Registry::isAuthorizedCall { policyId: 0, user };
434 let calldata = is_auth_call.abi_encode();
435 let result = registry.call(&calldata, Address::ZERO).unwrap();
436 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
437 assert!(!is_authorized);
438
439 let is_auth_call = ITIP403Registry::isAuthorizedCall { policyId: 1, user };
441 let calldata = is_auth_call.abi_encode();
442 let result = registry.call(&calldata, Address::ZERO).unwrap();
443 let is_authorized = bool::abi_decode(&result.bytes).unwrap();
444 assert!(is_authorized);
445
446 Ok(())
447 })
448 }
449
450 #[test]
451 fn test_invalid_selector() -> eyre::Result<()> {
452 let mut storage = HashMapStorageProvider::new(1).with_spec(TempoHardfork::Moderato);
453 let sender = Address::random();
454 StorageCtx::enter(&mut storage, || {
455 let mut registry = TIP403Registry::new();
456
457 let invalid_data = vec![0x12, 0x34, 0x56, 0x78];
459 let result = registry.call(&invalid_data, sender);
460 assert!(result.is_ok());
461 assert!(result.unwrap().reverted);
462
463 let short_data = vec![0x12, 0x34];
465 let result = registry.call(&short_data, sender);
466 assert!(result.is_err());
467
468 Ok(())
469 })
470 }
471
472 #[test]
473 fn test_create_multiple_policies() -> eyre::Result<()> {
474 let mut storage = HashMapStorageProvider::new(1);
475 let admin = Address::random();
476 StorageCtx::enter(&mut storage, || {
477 let mut registry = TIP403Registry::new();
478
479 let whitelist_call = ITIP403Registry::createPolicyCall {
481 admin,
482 policyType: ITIP403Registry::PolicyType::WHITELIST,
483 };
484 let calldata = whitelist_call.abi_encode();
485 let result = registry.call(&calldata, admin).unwrap();
486 let whitelist_id: u64 =
487 ITIP403Registry::createPolicyCall::abi_decode_returns(&result.bytes).unwrap();
488
489 let blacklist_call = ITIP403Registry::createPolicyCall {
490 admin,
491 policyType: ITIP403Registry::PolicyType::BLACKLIST,
492 };
493 let calldata = blacklist_call.abi_encode();
494 let result = registry.call(&calldata, admin).unwrap();
495 let blacklist_id: u64 =
496 ITIP403Registry::createPolicyCall::abi_decode_returns(&result.bytes).unwrap();
497
498 assert_eq!(whitelist_id, 2);
500 assert_eq!(blacklist_id, 3);
501
502 let counter_call = ITIP403Registry::policyIdCounterCall {};
504 let calldata = counter_call.abi_encode();
505 let result = registry.call(&calldata, admin).unwrap();
506 let counter = u64::abi_decode(&result.bytes).unwrap();
507 assert_eq!(counter, 4);
508
509 Ok(())
510 })
511 }
512
513 #[test]
514 fn test_selector_coverage() -> eyre::Result<()> {
515 let mut storage = HashMapStorageProvider::new(1);
516 StorageCtx::enter(&mut storage, || {
517 let mut registry = TIP403Registry::new();
518
519 let unsupported = check_selector_coverage(
520 &mut registry,
521 ITIP403RegistryCalls::SELECTORS,
522 "ITIP403Registry",
523 ITIP403RegistryCalls::name_by_selector,
524 );
525
526 assert_full_coverage([unsupported]);
527
528 Ok(())
529 })
530 }
531}