1pub mod dispatch;
9
10pub use slots as tip403_registry_slots;
12
13use crate::{
14 StorageCtx, is_precompile_address,
15 receive_policy_guard::{RECOVERY_ORIGINATOR, RecoveryMode},
16};
17pub use tempo_contracts::precompiles::{
18 ITIP403Registry::{self, PolicyType},
19 TIP403RegistryError, TIP403RegistryEvent,
20};
21use tempo_precompiles_macros::{Storable, contract};
22
23use crate::{
24 RECEIVE_POLICY_GUARD_ADDRESS, TIP403_REGISTRY_ADDRESS,
25 error::{Result, TempoPrecompileError},
26 storage::{Handler, Mapping},
27};
28use alloy::primitives::Address;
29use tempo_chainspec::hardfork::TempoHardfork;
30use tempo_primitives::TempoAddressExt;
31
32pub const REJECT_ALL_POLICY_ID: u64 = 0;
34
35pub const ALLOW_ALL_POLICY_ID: u64 = 1;
37
38pub const ALWAYS_AUTHORIZED: &[(TempoHardfork, &[Address])] =
40 &[(TempoHardfork::T6, &[RECEIVE_POLICY_GUARD_ADDRESS])];
41
42#[contract(addr = TIP403_REGISTRY_ADDRESS)]
50pub struct TIP403Registry {
51 policy_id_counter: u64,
55 policy_records: Mapping<u64, PolicyRecord>,
58 policy_set: Mapping<u64, Mapping<Address, bool>>,
62 receive_policies: Mapping<Address, ReceivePolicy>,
64}
65
66#[derive(Debug, Clone, Default, Storable)]
68struct ReceivePolicy {
69 config: ReceivePolicyConfig,
70 recovery_address: Address,
71}
72
73#[derive(Debug, Clone, Default, Storable)]
75struct ReceivePolicyConfig {
76 has_receive_policy: bool,
78 sender_policy_id: u64,
80 sender_policy_type: u8,
82 token_filter_id: u64,
84 token_filter_type: u8,
86 recovery_mode: RecoveryMode,
88}
89
90#[derive(Debug, Clone, Storable)]
94pub struct PolicyRecord {
95 pub base: PolicyData,
97 pub compound: CompoundPolicyData,
99}
100
101#[derive(Debug, Clone, Default, Storable)]
105pub struct CompoundPolicyData {
106 pub sender_policy_id: u64,
108 pub recipient_policy_id: u64,
110 pub mint_recipient_policy_id: u64,
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
119pub enum AuthRole {
120 Transfer,
122 Sender,
124 Recipient,
126 MintRecipient,
128}
129
130#[derive(Debug, Clone, Storable)]
132pub struct PolicyData {
133 pub policy_type: u8,
138 pub admin: Address,
140}
141
142impl PolicyData {
143 fn policy_type(&self) -> Result<PolicyType> {
145 let is_t2 = StorageCtx.spec().is_t2();
146
147 match self.policy_type.try_into() {
148 Ok(ty) if is_t2 || ty != PolicyType::COMPOUND => Ok(ty),
149 _ => Err(if is_t2 {
150 TIP403RegistryError::invalid_policy_type().into()
151 } else {
152 TempoPrecompileError::under_overflow()
153 }),
154 }
155 }
156
157 fn is_simple(&self) -> bool {
159 self.policy_type == PolicyType::WHITELIST as u8
160 || self.policy_type == PolicyType::BLACKLIST as u8
161 }
162
163 pub fn is_compound(&self) -> bool {
165 self.policy_type == PolicyType::COMPOUND as u8
166 }
167
168 fn is_default(&self) -> bool {
170 self.policy_type == 0 && self.admin == Address::ZERO
171 }
172}
173
174impl ReceivePolicyConfig {
175 fn sender_policy_data(&self) -> PolicyData {
176 PolicyData {
177 policy_type: self.sender_policy_type,
178 admin: Address::ZERO,
179 }
180 }
181
182 fn token_filter_data(&self) -> PolicyData {
183 PolicyData {
184 policy_type: self.token_filter_type,
185 admin: Address::ZERO,
186 }
187 }
188
189 fn sender_policy_type(&self) -> Result<PolicyType> {
190 self.sender_policy_type
191 .try_into()
192 .map_err(|_| TIP403RegistryError::invalid_receive_policy_type().into())
193 }
194
195 fn token_filter_type(&self) -> Result<PolicyType> {
196 self.token_filter_type
197 .try_into()
198 .map_err(|_| TIP403RegistryError::invalid_receive_policy_type().into())
199 }
200}
201
202impl TIP403Registry {
203 pub fn initialize(&mut self) -> Result<()> {
205 self.__initialize()
206 }
207
208 pub fn policy_id_counter(&self) -> Result<u64> {
210 self.policy_id_counter.read().map(|counter| counter.max(2))
212 }
213
214 pub fn policy_exists(&self, call: ITIP403Registry::policyExistsCall) -> Result<bool> {
216 if self.builtin_authorization(call.policyId).is_some() {
218 return Ok(true);
219 }
220
221 let counter = self.policy_id_counter()?;
223 Ok(call.policyId < counter)
224 }
225
226 pub fn policy_data(
233 &self,
234 call: ITIP403Registry::policyDataCall,
235 ) -> Result<ITIP403Registry::policyDataReturn> {
236 if self.storage.spec().is_t2() {
237 if self.builtin_authorization(call.policyId).is_some() {
241 return Ok(ITIP403Registry::policyDataReturn {
242 policyType: (call.policyId as u8)
243 .try_into()
244 .map_err(|_| TIP403RegistryError::invalid_policy_type())?,
245 admin: Address::ZERO,
246 });
247 }
248 } else {
249 if !self.policy_exists(ITIP403Registry::policyExistsCall {
251 policyId: call.policyId,
252 })? {
253 return Err(TIP403RegistryError::policy_not_found().into());
254 }
255 }
256
257 let data = self.get_policy_data(call.policyId)?;
259
260 Ok(ITIP403Registry::policyDataReturn {
261 policyType: data.policy_type()?,
262 admin: data.admin,
263 })
264 }
265
266 pub fn compound_policy_data(
274 &self,
275 call: ITIP403Registry::compoundPolicyDataCall,
276 ) -> Result<ITIP403Registry::compoundPolicyDataReturn> {
277 let data = self.get_policy_data(call.policyId)?;
278
279 if !data.is_compound() {
281 let err = if self.policy_exists(ITIP403Registry::policyExistsCall {
283 policyId: call.policyId,
284 })? {
285 TIP403RegistryError::incompatible_policy_type()
286 } else {
287 TIP403RegistryError::policy_not_found()
288 };
289 return Err(err.into());
290 }
291
292 let compound = self.policy_records[call.policyId].compound.read()?;
293 Ok(ITIP403Registry::compoundPolicyDataReturn {
294 senderPolicyId: compound.sender_policy_id,
295 recipientPolicyId: compound.recipient_policy_id,
296 mintRecipientPolicyId: compound.mint_recipient_policy_id,
297 })
298 }
299
300 pub fn receive_policy(&self, account: Address) -> Result<ITIP403Registry::receivePolicyReturn> {
302 let config = self.receive_policies[account].config.read()?;
303 Ok(ITIP403Registry::receivePolicyReturn {
304 hasReceivePolicy: config.has_receive_policy,
305 senderPolicyId: config.sender_policy_id,
306 senderPolicyType: config.sender_policy_type()?,
307 tokenFilterId: config.token_filter_id,
308 tokenFilterType: config.token_filter_type()?,
309 recoveryAuthority: self.receive_policy_recovery(account, config.recovery_mode)?,
310 })
311 }
312
313 pub fn validate_receive_policy(
316 &self,
317 token: Address,
318 sender: Address,
319 receiver: Address,
320 ) -> Result<Option<ITIP403Registry::BlockedReason>> {
321 Ok(self
322 .check_receive_policy(token, sender, receiver)?
323 .map(|(reason, _)| reason))
324 }
325
326 pub(crate) fn check_receive_policy(
329 &self,
330 token: Address,
331 sender: Address,
332 receiver: Address,
333 ) -> Result<Option<(ITIP403Registry::BlockedReason, Address)>> {
334 let config = self.receive_policies[receiver].config.read()?;
335 if !config.has_receive_policy {
336 return Ok(None);
337 }
338
339 let token_filter_data = config.token_filter_data();
340 if !self.is_authorized_simple(config.token_filter_id, token, Some(token_filter_data))? {
341 let recovery_address = self.receive_policy_recovery(receiver, config.recovery_mode)?;
342 return Ok(Some((
343 ITIP403Registry::BlockedReason::TOKEN_FILTER,
344 recovery_address,
345 )));
346 }
347
348 let sender_policy_data = config.sender_policy_data();
349 if !self.is_authorized_simple(config.sender_policy_id, sender, Some(sender_policy_data))? {
350 let recovery_address = self.receive_policy_recovery(receiver, config.recovery_mode)?;
351 return Ok(Some((
352 ITIP403Registry::BlockedReason::RECEIVE_POLICY,
353 recovery_address,
354 )));
355 }
356
357 Ok(None)
358 }
359
360 fn receive_policy_recovery(&self, account: Address, mode: RecoveryMode) -> Result<Address> {
363 match mode {
364 RecoveryMode::Originator => Ok(RECOVERY_ORIGINATOR),
365 RecoveryMode::Receiver => Ok(account),
366 RecoveryMode::ThirdParty => self.receive_policies[account].recovery_address.read(),
367 }
368 }
369
370 pub fn create_policy(
376 &mut self,
377 msg_sender: Address,
378 call: ITIP403Registry::createPolicyCall,
379 ) -> Result<u64> {
380 let policy_type = call.policyType.ensure_is_simple()?;
381
382 let new_policy_id = self.policy_id_counter()?;
383
384 self.policy_id_counter.write(
386 new_policy_id
387 .checked_add(1)
388 .ok_or(TempoPrecompileError::under_overflow())?,
389 )?;
390
391 self.policy_records[new_policy_id].base.write(PolicyData {
393 policy_type,
394 admin: call.admin,
395 })?;
396
397 self.emit_event(TIP403RegistryEvent::policy_created(
398 new_policy_id,
399 msg_sender,
400 policy_type.try_into().unwrap_or(PolicyType::__Invalid),
401 ))?;
402
403 self.emit_event(TIP403RegistryEvent::policy_admin_updated(
404 new_policy_id,
405 msg_sender,
406 call.admin,
407 ))?;
408
409 Ok(new_policy_id)
410 }
411
412 pub fn set_receive_policy(
414 &mut self,
415 msg_sender: Address,
416 call: ITIP403Registry::setReceivePolicyCall,
417 ) -> Result<()> {
418 if msg_sender.is_virtual() {
419 return Err(TIP403RegistryError::virtual_address_not_allowed().into());
420 }
421
422 let recovery_address = call.recoveryAuthority;
423 let (recovery_mode, recovery_write) = RecoveryMode::encode(recovery_address, msg_sender);
424
425 if is_precompile_address(recovery_address, self.storage.spec())
426 || recovery_address.is_virtual()
427 {
428 return Err(TIP403RegistryError::invalid_recovery_authority().into());
429 }
430
431 let sender_policy_type = self.validate_receive_policy_id(call.senderPolicyId)?;
432 let token_filter_type = self.validate_receive_policy_id(call.tokenFilterId)?;
433
434 let config = ReceivePolicyConfig {
435 has_receive_policy: true,
436 sender_policy_id: call.senderPolicyId,
437 sender_policy_type,
438 token_filter_id: call.tokenFilterId,
439 token_filter_type,
440 recovery_mode,
441 };
442 self.receive_policies[msg_sender].config.write(config)?;
443 self.receive_policies[msg_sender]
444 .recovery_address
445 .write(recovery_write)?;
446
447 self.emit_event(TIP403RegistryEvent::ReceivePolicyUpdated(
448 ITIP403Registry::ReceivePolicyUpdated {
449 account: msg_sender,
450 senderPolicyId: call.senderPolicyId,
451 tokenFilterId: call.tokenFilterId,
452 recoveryAuthority: recovery_address,
453 },
454 ))
455 }
456
457 pub fn create_policy_with_accounts(
465 &mut self,
466 msg_sender: Address,
467 call: ITIP403Registry::createPolicyWithAccountsCall,
468 ) -> Result<u64> {
469 let admin = call.admin;
470 let policy_type = call.policyType.ensure_is_simple()?;
471
472 if self.storage.spec().is_t3() {
474 for account in call.accounts.iter() {
475 if account.is_virtual() {
476 return Err(TIP403RegistryError::virtual_address_not_allowed().into());
477 }
478 }
479 }
480
481 let new_policy_id = self.policy_id_counter()?;
482
483 self.policy_id_counter.write(
485 new_policy_id
486 .checked_add(1)
487 .ok_or(TempoPrecompileError::under_overflow())?,
488 )?;
489
490 self.set_policy_data(new_policy_id, PolicyData { policy_type, admin })?;
492
493 for account in call.accounts.iter() {
496 self.set_policy_set(new_policy_id, *account, true)?;
497
498 match call.policyType {
499 PolicyType::WHITELIST => {
500 self.emit_event(TIP403RegistryEvent::whitelist_updated(
501 new_policy_id,
502 msg_sender,
503 *account,
504 true,
505 ))?;
506 }
507 PolicyType::BLACKLIST => {
508 self.emit_event(TIP403RegistryEvent::blacklist_updated(
509 new_policy_id,
510 msg_sender,
511 *account,
512 true,
513 ))?;
514 }
515 ITIP403Registry::PolicyType::COMPOUND | ITIP403Registry::PolicyType::__Invalid => {
516 return Err(TIP403RegistryError::incompatible_policy_type().into());
518 }
519 }
520 }
521
522 self.emit_event(TIP403RegistryEvent::policy_created(
523 new_policy_id,
524 msg_sender,
525 policy_type.try_into().unwrap_or(PolicyType::__Invalid),
526 ))?;
527
528 self.emit_event(TIP403RegistryEvent::policy_admin_updated(
529 new_policy_id,
530 msg_sender,
531 admin,
532 ))?;
533
534 Ok(new_policy_id)
535 }
536
537 pub fn set_policy_admin(
543 &mut self,
544 msg_sender: Address,
545 call: ITIP403Registry::setPolicyAdminCall,
546 ) -> Result<()> {
547 let data = self.get_policy_data(call.policyId)?;
548
549 if data.admin != msg_sender {
551 return Err(TIP403RegistryError::unauthorized().into());
552 }
553
554 self.set_policy_data(
556 call.policyId,
557 PolicyData {
558 admin: call.admin,
559 ..data
560 },
561 )?;
562
563 self.emit_event(TIP403RegistryEvent::policy_admin_updated(
564 call.policyId,
565 msg_sender,
566 call.admin,
567 ))
568 }
569
570 pub fn modify_policy_whitelist(
578 &mut self,
579 msg_sender: Address,
580 call: ITIP403Registry::modifyPolicyWhitelistCall,
581 ) -> Result<()> {
582 if self.storage.spec().is_t3() && call.account.is_virtual() {
584 return Err(TIP403RegistryError::virtual_address_not_allowed().into());
585 }
586
587 let data = self.get_policy_data(call.policyId)?;
588
589 if data.admin != msg_sender {
591 return Err(TIP403RegistryError::unauthorized().into());
592 }
593
594 if !matches!(data.policy_type()?, PolicyType::WHITELIST) {
596 return Err(TIP403RegistryError::incompatible_policy_type().into());
597 }
598
599 self.set_policy_set(call.policyId, call.account, call.allowed)?;
600
601 self.emit_event(TIP403RegistryEvent::whitelist_updated(
602 call.policyId,
603 msg_sender,
604 call.account,
605 call.allowed,
606 ))
607 }
608
609 pub fn modify_policy_blacklist(
617 &mut self,
618 msg_sender: Address,
619 call: ITIP403Registry::modifyPolicyBlacklistCall,
620 ) -> Result<()> {
621 if self.storage.spec().is_t3() && call.account.is_virtual() {
623 return Err(TIP403RegistryError::virtual_address_not_allowed().into());
624 }
625
626 let data = self.get_policy_data(call.policyId)?;
627
628 if data.admin != msg_sender {
630 return Err(TIP403RegistryError::unauthorized().into());
631 }
632
633 if !matches!(data.policy_type()?, PolicyType::BLACKLIST) {
635 return Err(TIP403RegistryError::incompatible_policy_type().into());
636 }
637
638 self.set_policy_set(call.policyId, call.account, call.restricted)?;
639
640 self.emit_event(TIP403RegistryEvent::blacklist_updated(
641 call.policyId,
642 msg_sender,
643 call.account,
644 call.restricted,
645 ))
646 }
647
648 pub fn create_compound_policy(
658 &mut self,
659 msg_sender: Address,
660 call: ITIP403Registry::createCompoundPolicyCall,
661 ) -> Result<u64> {
662 self.validate_simple_policy(call.senderPolicyId)?;
664 self.validate_simple_policy(call.recipientPolicyId)?;
665 self.validate_simple_policy(call.mintRecipientPolicyId)?;
666
667 let new_policy_id = self.policy_id_counter()?;
668
669 self.policy_id_counter.write(
671 new_policy_id
672 .checked_add(1)
673 .ok_or(TempoPrecompileError::under_overflow())?,
674 )?;
675
676 self.policy_records[new_policy_id].write(PolicyRecord {
678 base: PolicyData {
679 policy_type: PolicyType::COMPOUND as u8,
680 admin: Address::ZERO,
681 },
682 compound: CompoundPolicyData {
683 sender_policy_id: call.senderPolicyId,
684 recipient_policy_id: call.recipientPolicyId,
685 mint_recipient_policy_id: call.mintRecipientPolicyId,
686 },
687 })?;
688
689 self.emit_event(TIP403RegistryEvent::compound_policy_created(
691 new_policy_id,
692 msg_sender,
693 call.senderPolicyId,
694 call.recipientPolicyId,
695 call.mintRecipientPolicyId,
696 ))?;
697
698 Ok(new_policy_id)
699 }
700
701 pub fn is_authorized_as(&self, policy_id: u64, user: Address, role: AuthRole) -> Result<bool> {
713 let hardfork = self.storage.spec();
714
715 if ALWAYS_AUTHORIZED
717 .iter()
718 .any(|(fork, addrs)| hardfork >= *fork && addrs.contains(&user))
719 {
720 return Ok(true);
721 }
722
723 if let Some(auth) = self.builtin_authorization(policy_id) {
724 return Ok(auth);
725 }
726
727 let data = self.get_policy_data(policy_id)?;
728
729 if data.is_compound() {
730 let compound = self.policy_records[policy_id].compound.read()?;
731 return match role {
732 AuthRole::Sender => {
733 self.is_authorized_simple(compound.sender_policy_id, user, None)
734 }
735 AuthRole::Recipient => {
736 self.is_authorized_simple(compound.recipient_policy_id, user, None)
737 }
738 AuthRole::MintRecipient => {
739 self.is_authorized_simple(compound.mint_recipient_policy_id, user, None)
740 }
741 AuthRole::Transfer => {
742 let sender_auth =
744 self.is_authorized_simple(compound.sender_policy_id, user, None)?;
745 if hardfork.is_t2() && !sender_auth {
746 return Ok(false);
747 }
748 let recipient_auth =
749 self.is_authorized_simple(compound.recipient_policy_id, user, None)?;
750 Ok(sender_auth && recipient_auth)
751 }
752 };
753 }
754
755 self.is_authorized_simple_inner(policy_id, user, &data)
756 }
757
758 #[inline]
761 fn builtin_authorization(&self, policy_id: u64) -> Option<bool> {
762 match policy_id {
763 ALLOW_ALL_POLICY_ID => Some(true),
764 REJECT_ALL_POLICY_ID => Some(false),
765 _ => None,
766 }
767 }
768
769 fn is_authorized_simple(
773 &self,
774 policy_id: u64,
775 user: Address,
776 cache: Option<PolicyData>,
777 ) -> Result<bool> {
778 if let Some(auth) = self.builtin_authorization(policy_id) {
779 return Ok(auth);
780 }
781 let data = match cache {
782 Some(data) => data,
783 None => self.get_policy_data(policy_id)?,
784 };
785 self.is_authorized_simple_inner(policy_id, user, &data)
786 }
787
788 fn is_authorized_simple_inner(
790 &self,
791 policy_id: u64,
792 user: Address,
793 data: &PolicyData,
794 ) -> Result<bool> {
795 let is_in_set = self.policy_set[policy_id][user].read()?;
799
800 match data.policy_type()? {
801 PolicyType::WHITELIST => Ok(is_in_set),
802 PolicyType::BLACKLIST => Ok(!is_in_set),
803 PolicyType::COMPOUND => Err(TIP403RegistryError::incompatible_policy_type().into()),
804 PolicyType::__Invalid => unreachable!(),
805 }
806 }
807
808 fn validate_simple_policy(&self, policy_id: u64) -> Result<()> {
810 if self.builtin_authorization(policy_id).is_some() {
812 return Ok(());
813 }
814
815 if policy_id >= self.policy_id_counter()? {
817 return Err(TIP403RegistryError::policy_not_found().into());
818 }
819
820 let data = self.get_policy_data(policy_id)?;
822 if !data.is_simple() {
823 return Err(TIP403RegistryError::policy_not_simple().into());
824 }
825
826 Ok(())
827 }
828
829 fn validate_receive_policy_id(&self, policy_id: u64) -> Result<u8> {
832 if self.builtin_authorization(policy_id).is_some() {
833 return Ok(policy_id as u8); }
835 if policy_id >= self.policy_id_counter()? {
836 return Err(TIP403RegistryError::policy_not_found().into());
837 }
838 let data = self.get_policy_data(policy_id)?;
839 if !data.is_simple() {
840 return Err(TIP403RegistryError::invalid_receive_policy_type().into());
841 }
842 Ok(data.policy_type)
843 }
844
845 fn get_policy_data(&self, policy_id: u64) -> Result<PolicyData> {
850 let data = self.policy_records[policy_id].base.read()?;
851
852 if self.storage.spec().is_t2()
855 && data.is_default()
856 && policy_id >= self.policy_id_counter()?
857 {
858 return Err(TIP403RegistryError::policy_not_found().into());
859 }
860
861 Ok(data)
862 }
863
864 fn set_policy_data(&mut self, policy_id: u64, data: PolicyData) -> Result<()> {
869 self.policy_records[policy_id].base.write(data)
870 }
871
872 fn set_policy_set(&mut self, policy_id: u64, account: Address, value: bool) -> Result<()> {
873 self.policy_set[policy_id][account].write(value)
874 }
875}
876
877impl AuthRole {
878 #[inline]
879 fn transfer_or(t2_variant: Self) -> Self {
880 if StorageCtx.spec().is_t2() {
881 t2_variant
882 } else {
883 Self::Transfer
884 }
885 }
886
887 pub fn transfer() -> Self {
889 Self::Transfer
890 }
891
892 pub fn sender() -> Self {
894 Self::transfer_or(Self::Sender)
895 }
896
897 pub fn recipient() -> Self {
899 Self::transfer_or(Self::Recipient)
900 }
901
902 pub fn mint_recipient() -> Self {
904 Self::transfer_or(Self::MintRecipient)
905 }
906}
907
908pub fn is_policy_lookup_error(e: &TempoPrecompileError) -> bool {
911 if StorageCtx.spec().is_t2() {
912 *e == TIP403RegistryError::invalid_policy_type().into()
914 || *e == TIP403RegistryError::policy_not_found().into()
915 } else {
916 *e == TempoPrecompileError::under_overflow()
918 }
919}
920
921trait PolicyTypeExt {
923 fn ensure_is_simple(&self) -> Result<u8>;
925}
926
927impl PolicyTypeExt for PolicyType {
928 fn ensure_is_simple(&self) -> Result<u8> {
933 match self {
934 Self::WHITELIST | Self::BLACKLIST => Ok(*self as u8),
935 Self::COMPOUND | Self::__Invalid => {
936 if StorageCtx.spec().is_t2() {
937 Err(TIP403RegistryError::incompatible_policy_type().into())
938 } else {
939 Ok(Self::__Invalid as u8)
940 }
941 }
942 }
943 }
944}
945
946#[cfg(test)]
947mod tests {
948 use super::*;
949 use crate::{
950 SYSTEM_PRECOMPILES,
951 error::TempoPrecompileError,
952 storage::{ContractStorage, StorageCtx, hashmap::HashMapStorageProvider},
953 };
954 use alloy::{
955 primitives::{Address, Log},
956 sol_types::SolEvent,
957 };
958 use rand_08::Rng;
959 use tempo_contracts::precompiles::{PATH_USD_ADDRESS, TIP403_REGISTRY_ADDRESS};
960 use tempo_primitives::{MasterId, TempoAddressExt, UserTag};
961
962 #[test]
963 fn test_create_policy() -> eyre::Result<()> {
964 let mut storage = HashMapStorageProvider::new(1);
965 let admin = Address::random();
966 StorageCtx::enter(&mut storage, || {
967 let mut registry = TIP403Registry::new();
968
969 assert_eq!(registry.policy_id_counter()?, 2);
971
972 let result = registry.create_policy(
974 admin,
975 ITIP403Registry::createPolicyCall {
976 admin,
977 policyType: ITIP403Registry::PolicyType::WHITELIST,
978 },
979 );
980 assert!(result.is_ok());
981 assert_eq!(result?, 2);
982
983 assert_eq!(registry.policy_id_counter()?, 3);
985
986 let data = registry.policy_data(ITIP403Registry::policyDataCall { policyId: 2 })?;
988 assert_eq!(data.policyType, ITIP403Registry::PolicyType::WHITELIST);
989 assert_eq!(data.admin, admin);
990 Ok(())
991 })
992 }
993
994 #[test]
995 fn test_is_authorized_special_policies() -> eyre::Result<()> {
996 let mut storage = HashMapStorageProvider::new(1);
997 let user = Address::random();
998 StorageCtx::enter(&mut storage, || {
999 let registry = TIP403Registry::new();
1000
1001 assert!(!registry.is_authorized_as(0, user, AuthRole::Transfer)?);
1003
1004 assert!(registry.is_authorized_as(1, user, AuthRole::Transfer)?);
1006 Ok(())
1007 })
1008 }
1009
1010 #[test]
1011 fn test_always_authorized_t6_bypasses_policy_variants() -> eyre::Result<()> {
1012 const ROLES: &[AuthRole] = &[
1013 AuthRole::Transfer,
1014 AuthRole::Sender,
1015 AuthRole::Recipient,
1016 AuthRole::MintRecipient,
1017 ];
1018 let admin = Address::random();
1019
1020 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
1022 StorageCtx::enter(&mut storage, || {
1023 let registry = TIP403Registry::new();
1024 assert!(!registry.is_authorized_as(
1025 REJECT_ALL_POLICY_ID,
1026 RECEIVE_POLICY_GUARD_ADDRESS,
1027 AuthRole::Transfer,
1028 )?);
1029 Ok::<(), TempoPrecompileError>(())
1030 })?;
1031
1032 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T6);
1034 StorageCtx::enter(&mut storage, || {
1035 let mut registry = TIP403Registry::new();
1036 let whitelist_id = registry.create_policy(
1037 admin,
1038 ITIP403Registry::createPolicyCall {
1039 admin,
1040 policyType: ITIP403Registry::PolicyType::WHITELIST,
1041 },
1042 )?;
1043 let blacklist_id = registry.create_policy(
1044 admin,
1045 ITIP403Registry::createPolicyCall {
1046 admin,
1047 policyType: ITIP403Registry::PolicyType::BLACKLIST,
1048 },
1049 )?;
1050 let compound_id = registry.create_compound_policy(
1051 admin,
1052 ITIP403Registry::createCompoundPolicyCall {
1053 senderPolicyId: REJECT_ALL_POLICY_ID,
1054 recipientPolicyId: REJECT_ALL_POLICY_ID,
1055 mintRecipientPolicyId: REJECT_ALL_POLICY_ID,
1056 },
1057 )?;
1058
1059 registry.modify_policy_blacklist(
1060 admin,
1061 ITIP403Registry::modifyPolicyBlacklistCall {
1062 policyId: blacklist_id,
1063 account: RECEIVE_POLICY_GUARD_ADDRESS,
1064 restricted: true,
1065 },
1066 )?;
1067 for (policy_id, roles) in [
1068 (REJECT_ALL_POLICY_ID, ROLES),
1069 (whitelist_id, &[AuthRole::Transfer][..]),
1070 (blacklist_id, &[AuthRole::Transfer][..]),
1071 (compound_id, ROLES),
1072 ] {
1073 for role in roles {
1074 assert!(registry.is_authorized_as(
1075 policy_id,
1076 RECEIVE_POLICY_GUARD_ADDRESS,
1077 *role,
1078 )?);
1079 }
1080 }
1081
1082 Ok(())
1083 })
1084 }
1085
1086 #[test]
1087 fn test_whitelist_policy() -> eyre::Result<()> {
1088 let mut storage = HashMapStorageProvider::new(1);
1089 let admin = Address::random();
1090 let user = Address::random();
1091 StorageCtx::enter(&mut storage, || {
1092 let mut registry = TIP403Registry::new();
1093
1094 let policy_id = registry.create_policy(
1096 admin,
1097 ITIP403Registry::createPolicyCall {
1098 admin,
1099 policyType: ITIP403Registry::PolicyType::WHITELIST,
1100 },
1101 )?;
1102
1103 assert!(!registry.is_authorized_as(policy_id, user, AuthRole::Transfer)?);
1105
1106 registry.modify_policy_whitelist(
1108 admin,
1109 ITIP403Registry::modifyPolicyWhitelistCall {
1110 policyId: policy_id,
1111 account: user,
1112 allowed: true,
1113 },
1114 )?;
1115
1116 assert!(registry.is_authorized_as(policy_id, user, AuthRole::Transfer)?);
1118
1119 Ok(())
1120 })
1121 }
1122
1123 #[test]
1124 fn test_blacklist_policy() -> eyre::Result<()> {
1125 let mut storage = HashMapStorageProvider::new(1);
1126 let admin = Address::random();
1127 let user = Address::random();
1128 StorageCtx::enter(&mut storage, || {
1129 let mut registry = TIP403Registry::new();
1130
1131 let policy_id = registry.create_policy(
1133 admin,
1134 ITIP403Registry::createPolicyCall {
1135 admin,
1136 policyType: ITIP403Registry::PolicyType::BLACKLIST,
1137 },
1138 )?;
1139
1140 assert!(registry.is_authorized_as(policy_id, user, AuthRole::Transfer)?);
1142
1143 registry.modify_policy_blacklist(
1145 admin,
1146 ITIP403Registry::modifyPolicyBlacklistCall {
1147 policyId: policy_id,
1148 account: user,
1149 restricted: true,
1150 },
1151 )?;
1152
1153 assert!(!registry.is_authorized_as(policy_id, user, AuthRole::Transfer)?);
1155
1156 Ok(())
1157 })
1158 }
1159
1160 #[test]
1161 fn test_policy_data_reverts_for_non_existent_policy() -> eyre::Result<()> {
1162 let mut storage = HashMapStorageProvider::new(1);
1163 StorageCtx::enter(&mut storage, || {
1164 let registry = TIP403Registry::new();
1165
1166 let result = registry.policy_data(ITIP403Registry::policyDataCall { policyId: 100 });
1168 assert!(result.is_err());
1169
1170 assert!(matches!(
1172 result.unwrap_err(),
1173 TempoPrecompileError::TIP403RegistryError(TIP403RegistryError::PolicyNotFound(_))
1174 ));
1175
1176 Ok(())
1177 })
1178 }
1179
1180 #[test]
1181 fn test_policy_data_builtin_policies_boundary() -> eyre::Result<()> {
1182 for (hardfork, expect_allow_all_type) in [
1183 (TempoHardfork::T1C, ITIP403Registry::PolicyType::WHITELIST),
1185 (TempoHardfork::T2, ITIP403Registry::PolicyType::BLACKLIST),
1187 ] {
1188 let mut storage = HashMapStorageProvider::new_with_spec(1, hardfork);
1189 StorageCtx::enter(&mut storage, || {
1190 let registry = TIP403Registry::new();
1191
1192 let reject = registry.policy_data(ITIP403Registry::policyDataCall {
1194 policyId: REJECT_ALL_POLICY_ID,
1195 })?;
1196 assert_eq!(reject.policyType, ITIP403Registry::PolicyType::WHITELIST);
1197 assert_eq!(reject.admin, Address::ZERO);
1198
1199 let allow = registry.policy_data(ITIP403Registry::policyDataCall {
1201 policyId: ALLOW_ALL_POLICY_ID,
1202 })?;
1203 assert_eq!(allow.policyType, expect_allow_all_type);
1204 assert_eq!(allow.admin, Address::ZERO);
1205
1206 Ok::<_, TempoPrecompileError>(())
1207 })?;
1208 }
1209 Ok(())
1210 }
1211
1212 #[test]
1213 fn test_receive_policy_defaults_to_none() -> eyre::Result<()> {
1214 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T6);
1215 let account = Address::random();
1216 StorageCtx::enter(&mut storage, || {
1217 let registry = TIP403Registry::new();
1218
1219 let policy = registry.receive_policy(account)?;
1220 assert!(!policy.hasReceivePolicy);
1221 assert_eq!(policy.senderPolicyId, REJECT_ALL_POLICY_ID);
1222 assert_eq!(
1223 policy.senderPolicyType,
1224 ITIP403Registry::PolicyType::WHITELIST
1225 );
1226 assert_eq!(policy.tokenFilterId, REJECT_ALL_POLICY_ID);
1227 assert_eq!(
1228 policy.tokenFilterType,
1229 ITIP403Registry::PolicyType::WHITELIST
1230 );
1231 assert_eq!(policy.recoveryAuthority, Address::ZERO);
1232
1233 assert_eq!(
1234 registry.validate_receive_policy(Address::random(), Address::random(), account)?,
1235 None
1236 );
1237 assert_eq!(
1238 registry.receive_policies[account].recovery_address.read()?,
1239 Address::ZERO
1240 );
1241
1242 Ok(())
1243 })
1244 }
1245
1246 #[test]
1247 fn test_set_receive_policy_stores_config() -> eyre::Result<()> {
1248 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T6);
1249 let account = Address::random();
1250 let recovery = Address::random();
1251 StorageCtx::enter(&mut storage, || {
1252 let mut registry = TIP403Registry::new();
1253
1254 registry.set_receive_policy(
1255 account,
1256 ITIP403Registry::setReceivePolicyCall {
1257 senderPolicyId: REJECT_ALL_POLICY_ID,
1258 tokenFilterId: ALLOW_ALL_POLICY_ID,
1259 recoveryAuthority: recovery,
1260 },
1261 )?;
1262
1263 let policy = registry.receive_policy(account)?;
1264 assert!(policy.hasReceivePolicy);
1265 assert_eq!(policy.senderPolicyId, REJECT_ALL_POLICY_ID);
1266 assert_eq!(
1267 policy.senderPolicyType,
1268 ITIP403Registry::PolicyType::WHITELIST
1269 );
1270 assert_eq!(policy.tokenFilterId, ALLOW_ALL_POLICY_ID);
1271 assert_eq!(
1272 policy.tokenFilterType,
1273 ITIP403Registry::PolicyType::BLACKLIST
1274 );
1275 assert_eq!(policy.recoveryAuthority, recovery);
1276 assert_eq!(
1277 registry.receive_policies[account].recovery_address.read()?,
1278 recovery
1279 );
1280
1281 registry.assert_emitted_events(vec![TIP403RegistryEvent::ReceivePolicyUpdated(
1282 ITIP403Registry::ReceivePolicyUpdated {
1283 account,
1284 senderPolicyId: REJECT_ALL_POLICY_ID,
1285 tokenFilterId: ALLOW_ALL_POLICY_ID,
1286 recoveryAuthority: recovery,
1287 },
1288 )]);
1289
1290 Ok(())
1291 })
1292 }
1293
1294 #[test]
1295 fn test_set_receive_policy_rejects_virtual_account() -> eyre::Result<()> {
1296 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T6);
1297 StorageCtx::enter(&mut storage, || {
1298 let mut registry = TIP403Registry::new();
1299
1300 let virtual_result = registry.set_receive_policy(
1301 Address::new_virtual(MasterId::ZERO, UserTag::ZERO),
1302 ITIP403Registry::setReceivePolicyCall {
1303 senderPolicyId: REJECT_ALL_POLICY_ID,
1304 tokenFilterId: ALLOW_ALL_POLICY_ID,
1305 recoveryAuthority: Address::ZERO,
1306 },
1307 );
1308 assert!(matches!(
1309 virtual_result,
1310 Err(TempoPrecompileError::TIP403RegistryError(
1311 TIP403RegistryError::VirtualAddressNotAllowed(_)
1312 ))
1313 ));
1314
1315 Ok(())
1316 })
1317 }
1318
1319 #[test]
1320 fn test_set_receive_policy_rejects_invalid_recovery_address() -> eyre::Result<()> {
1321 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T6);
1322 let account = Address::random();
1323 let virtual_addr = Address::new_virtual(MasterId::random(), UserTag::random());
1324
1325 StorageCtx::enter(&mut storage, || {
1326 let mut registry = TIP403Registry::new();
1327
1328 let rejected = SYSTEM_PRECOMPILES
1329 .iter()
1330 .filter_map(|&(address, hf)| (hf <= StorageCtx.spec()).then_some(address))
1331 .chain([PATH_USD_ADDRESS, virtual_addr]);
1332
1333 for recovery_address in rejected {
1334 let result = registry.set_receive_policy(
1335 account,
1336 ITIP403Registry::setReceivePolicyCall {
1337 senderPolicyId: REJECT_ALL_POLICY_ID,
1338 tokenFilterId: ALLOW_ALL_POLICY_ID,
1339 recoveryAuthority: recovery_address,
1340 },
1341 );
1342 assert_eq!(
1343 result.unwrap_err(),
1344 TIP403RegistryError::invalid_recovery_authority().into()
1345 );
1346 }
1347
1348 registry.set_receive_policy(
1350 account,
1351 ITIP403Registry::setReceivePolicyCall {
1352 senderPolicyId: REJECT_ALL_POLICY_ID,
1353 tokenFilterId: ALLOW_ALL_POLICY_ID,
1354 recoveryAuthority: Address::ZERO,
1355 },
1356 )?;
1357 registry.set_receive_policy(
1358 account,
1359 ITIP403Registry::setReceivePolicyCall {
1360 senderPolicyId: REJECT_ALL_POLICY_ID,
1361 tokenFilterId: ALLOW_ALL_POLICY_ID,
1362 recoveryAuthority: account,
1363 },
1364 )?;
1365 let policy = registry.receive_policy(account)?;
1366 assert_eq!(policy.recoveryAuthority, account);
1367 assert_eq!(
1368 registry.receive_policies[account].recovery_address.read()?,
1369 Address::ZERO
1370 );
1371
1372 Ok(())
1373 })
1374 }
1375
1376 #[test]
1377 fn test_set_receive_policy_rejects_invalid_policy() -> eyre::Result<()> {
1378 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T6);
1379 let account = Address::random();
1380 let creator = Address::random();
1381 StorageCtx::enter(&mut storage, || {
1382 let mut registry = TIP403Registry::new();
1383
1384 let missing_result = registry.set_receive_policy(
1385 account,
1386 ITIP403Registry::setReceivePolicyCall {
1387 senderPolicyId: 99,
1388 tokenFilterId: ALLOW_ALL_POLICY_ID,
1389 recoveryAuthority: Address::ZERO,
1390 },
1391 );
1392 assert!(matches!(
1393 missing_result,
1394 Err(TempoPrecompileError::TIP403RegistryError(
1395 TIP403RegistryError::PolicyNotFound(_)
1396 ))
1397 ));
1398
1399 let compound_id = registry.create_compound_policy(
1400 creator,
1401 ITIP403Registry::createCompoundPolicyCall {
1402 senderPolicyId: REJECT_ALL_POLICY_ID,
1403 recipientPolicyId: ALLOW_ALL_POLICY_ID,
1404 mintRecipientPolicyId: ALLOW_ALL_POLICY_ID,
1405 },
1406 )?;
1407 let compound_result = registry.set_receive_policy(
1408 account,
1409 ITIP403Registry::setReceivePolicyCall {
1410 senderPolicyId: compound_id,
1411 tokenFilterId: ALLOW_ALL_POLICY_ID,
1412 recoveryAuthority: Address::ZERO,
1413 },
1414 );
1415 assert!(matches!(
1416 compound_result,
1417 Err(TempoPrecompileError::TIP403RegistryError(
1418 TIP403RegistryError::InvalidReceivePolicyType(_)
1419 ))
1420 ));
1421
1422 Ok(())
1423 })
1424 }
1425
1426 #[test]
1427 fn test_validate_receive_policy_reports_token_filter_first() -> eyre::Result<()> {
1428 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T6);
1429 let receiver = Address::random();
1430 StorageCtx::enter(&mut storage, || {
1431 let mut registry = TIP403Registry::new();
1432 registry.set_receive_policy(
1433 receiver,
1434 ITIP403Registry::setReceivePolicyCall {
1435 senderPolicyId: REJECT_ALL_POLICY_ID,
1436 tokenFilterId: REJECT_ALL_POLICY_ID,
1437 recoveryAuthority: Address::ZERO,
1438 },
1439 )?;
1440
1441 assert_eq!(
1442 registry.validate_receive_policy(Address::random(), Address::random(), receiver)?,
1443 Some(ITIP403Registry::BlockedReason::TOKEN_FILTER)
1444 );
1445
1446 Ok(())
1447 })
1448 }
1449
1450 #[test]
1451 fn test_validate_receive_policy_reports_sender_policy() -> eyre::Result<()> {
1452 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T6);
1453 let receiver = Address::random();
1454 StorageCtx::enter(&mut storage, || {
1455 let mut registry = TIP403Registry::new();
1456 registry.set_receive_policy(
1457 receiver,
1458 ITIP403Registry::setReceivePolicyCall {
1459 senderPolicyId: REJECT_ALL_POLICY_ID,
1460 tokenFilterId: ALLOW_ALL_POLICY_ID,
1461 recoveryAuthority: Address::ZERO,
1462 },
1463 )?;
1464
1465 assert_eq!(
1466 registry.validate_receive_policy(Address::random(), Address::random(), receiver)?,
1467 Some(ITIP403Registry::BlockedReason::RECEIVE_POLICY)
1468 );
1469
1470 Ok(())
1471 })
1472 }
1473
1474 #[test]
1475 fn test_policy_exists() -> eyre::Result<()> {
1476 let mut storage = HashMapStorageProvider::new(1);
1477 let admin = Address::random();
1478 StorageCtx::enter(&mut storage, || {
1479 let mut registry = TIP403Registry::new();
1480
1481 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall { policyId: 0 })?);
1483 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall { policyId: 1 })?);
1484
1485 let mut rng = rand_08::thread_rng();
1487 for _ in 0..100 {
1488 let random_policy_id = rng.gen_range(2..u64::MAX);
1489 assert!(!registry.policy_exists(ITIP403Registry::policyExistsCall {
1490 policyId: random_policy_id
1491 })?);
1492 }
1493
1494 let mut created_policy_ids = Vec::new();
1496 for i in 0..50 {
1497 let policy_id = registry.create_policy(
1498 admin,
1499 ITIP403Registry::createPolicyCall {
1500 admin,
1501 policyType: if i % 2 == 0 {
1502 ITIP403Registry::PolicyType::WHITELIST
1503 } else {
1504 ITIP403Registry::PolicyType::BLACKLIST
1505 },
1506 },
1507 )?;
1508 created_policy_ids.push(policy_id);
1509 }
1510
1511 for policy_id in &created_policy_ids {
1513 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall {
1514 policyId: *policy_id
1515 })?);
1516 }
1517
1518 Ok(())
1519 })
1520 }
1521
1522 #[test]
1527 fn test_create_compound_policy() -> eyre::Result<()> {
1528 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
1529 let admin = Address::random();
1530 let creator = Address::random();
1531 StorageCtx::enter(&mut storage, || {
1532 let mut registry = TIP403Registry::new();
1533
1534 let sender_policy = registry.create_policy(
1536 admin,
1537 ITIP403Registry::createPolicyCall {
1538 admin,
1539 policyType: ITIP403Registry::PolicyType::WHITELIST,
1540 },
1541 )?;
1542 let recipient_policy = registry.create_policy(
1543 admin,
1544 ITIP403Registry::createPolicyCall {
1545 admin,
1546 policyType: ITIP403Registry::PolicyType::BLACKLIST,
1547 },
1548 )?;
1549 let mint_recipient_policy = registry.create_policy(
1550 admin,
1551 ITIP403Registry::createPolicyCall {
1552 admin,
1553 policyType: ITIP403Registry::PolicyType::WHITELIST,
1554 },
1555 )?;
1556
1557 let compound_id = registry.create_compound_policy(
1559 creator,
1560 ITIP403Registry::createCompoundPolicyCall {
1561 senderPolicyId: sender_policy,
1562 recipientPolicyId: recipient_policy,
1563 mintRecipientPolicyId: mint_recipient_policy,
1564 },
1565 )?;
1566
1567 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall {
1569 policyId: compound_id
1570 })?);
1571
1572 let data = registry.policy_data(ITIP403Registry::policyDataCall {
1574 policyId: compound_id,
1575 })?;
1576 assert_eq!(data.policyType, ITIP403Registry::PolicyType::COMPOUND);
1577 assert_eq!(data.admin, Address::ZERO); let compound_data =
1581 registry.compound_policy_data(ITIP403Registry::compoundPolicyDataCall {
1582 policyId: compound_id,
1583 })?;
1584 assert_eq!(compound_data.senderPolicyId, sender_policy);
1585 assert_eq!(compound_data.recipientPolicyId, recipient_policy);
1586 assert_eq!(compound_data.mintRecipientPolicyId, mint_recipient_policy);
1587
1588 Ok(())
1589 })
1590 }
1591
1592 #[test]
1593 fn test_compound_policy_rejects_non_existent_refs() -> eyre::Result<()> {
1594 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1595 let creator = Address::random();
1596 StorageCtx::enter(&mut storage, || {
1597 let mut registry = TIP403Registry::new();
1598
1599 let result = registry.create_compound_policy(
1601 creator,
1602 ITIP403Registry::createCompoundPolicyCall {
1603 senderPolicyId: 999,
1604 recipientPolicyId: 1,
1605 mintRecipientPolicyId: 1,
1606 },
1607 );
1608 assert!(result.is_err());
1609
1610 Ok(())
1611 })
1612 }
1613
1614 #[test]
1615 fn test_compound_policy_rejects_compound_refs() -> eyre::Result<()> {
1616 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1617 let admin = Address::random();
1618 let creator = Address::random();
1619 StorageCtx::enter(&mut storage, || {
1620 let mut registry = TIP403Registry::new();
1621
1622 let simple_policy = registry.create_policy(
1624 admin,
1625 ITIP403Registry::createPolicyCall {
1626 admin,
1627 policyType: ITIP403Registry::PolicyType::WHITELIST,
1628 },
1629 )?;
1630
1631 let compound_id = registry.create_compound_policy(
1633 creator,
1634 ITIP403Registry::createCompoundPolicyCall {
1635 senderPolicyId: 1,
1636 recipientPolicyId: simple_policy,
1637 mintRecipientPolicyId: 1,
1638 },
1639 )?;
1640
1641 let result = registry.create_compound_policy(
1643 creator,
1644 ITIP403Registry::createCompoundPolicyCall {
1645 senderPolicyId: compound_id, recipientPolicyId: 1,
1647 mintRecipientPolicyId: 1,
1648 },
1649 );
1650 assert!(result.is_err());
1651
1652 Ok(())
1653 })
1654 }
1655
1656 #[test]
1657 fn test_compound_policy_sender_recipient_differentiation() -> eyre::Result<()> {
1658 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1659 let admin = Address::random();
1660 let creator = Address::random();
1661 let alice = Address::random();
1662 let bob = Address::random();
1663 StorageCtx::enter(&mut storage, || {
1664 let mut registry = TIP403Registry::new();
1665
1666 let sender_policy = registry.create_policy(
1668 admin,
1669 ITIP403Registry::createPolicyCall {
1670 admin,
1671 policyType: ITIP403Registry::PolicyType::WHITELIST,
1672 },
1673 )?;
1674 registry.modify_policy_whitelist(
1675 admin,
1676 ITIP403Registry::modifyPolicyWhitelistCall {
1677 policyId: sender_policy,
1678 account: alice,
1679 allowed: true,
1680 },
1681 )?;
1682
1683 let recipient_policy = registry.create_policy(
1685 admin,
1686 ITIP403Registry::createPolicyCall {
1687 admin,
1688 policyType: ITIP403Registry::PolicyType::WHITELIST,
1689 },
1690 )?;
1691 registry.modify_policy_whitelist(
1692 admin,
1693 ITIP403Registry::modifyPolicyWhitelistCall {
1694 policyId: recipient_policy,
1695 account: bob,
1696 allowed: true,
1697 },
1698 )?;
1699
1700 let compound_id = registry.create_compound_policy(
1702 creator,
1703 ITIP403Registry::createCompoundPolicyCall {
1704 senderPolicyId: sender_policy,
1705 recipientPolicyId: recipient_policy,
1706 mintRecipientPolicyId: 1, },
1708 )?;
1709
1710 assert!(registry.is_authorized_as(compound_id, alice, AuthRole::Sender)?);
1712
1713 assert!(!registry.is_authorized_as(compound_id, bob, AuthRole::Sender)?);
1715
1716 assert!(registry.is_authorized_as(compound_id, bob, AuthRole::Recipient)?);
1718
1719 assert!(!registry.is_authorized_as(compound_id, alice, AuthRole::Recipient)?);
1721
1722 assert!(registry.is_authorized_as(compound_id, alice, AuthRole::MintRecipient)?);
1724 assert!(registry.is_authorized_as(compound_id, bob, AuthRole::MintRecipient)?);
1725
1726 Ok(())
1727 })
1728 }
1729
1730 #[test]
1731 fn test_compound_policy_is_authorized_behavior() -> eyre::Result<()> {
1732 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1733 let admin = Address::random();
1734 let creator = Address::random();
1735 let user = Address::random();
1736 StorageCtx::enter(&mut storage, || {
1737 let mut registry = TIP403Registry::new();
1738
1739 let sender_policy = registry.create_policy(
1741 admin,
1742 ITIP403Registry::createPolicyCall {
1743 admin,
1744 policyType: ITIP403Registry::PolicyType::WHITELIST,
1745 },
1746 )?;
1747 registry.modify_policy_whitelist(
1748 admin,
1749 ITIP403Registry::modifyPolicyWhitelistCall {
1750 policyId: sender_policy,
1751 account: user,
1752 allowed: true,
1753 },
1754 )?;
1755
1756 let recipient_policy = registry.create_policy(
1758 admin,
1759 ITIP403Registry::createPolicyCall {
1760 admin,
1761 policyType: ITIP403Registry::PolicyType::WHITELIST,
1762 },
1763 )?;
1764
1765 let compound_id = registry.create_compound_policy(
1767 creator,
1768 ITIP403Registry::createCompoundPolicyCall {
1769 senderPolicyId: sender_policy,
1770 recipientPolicyId: recipient_policy,
1771 mintRecipientPolicyId: 1,
1772 },
1773 )?;
1774
1775 assert!(registry.is_authorized_as(compound_id, user, AuthRole::Sender)?);
1778 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Recipient)?);
1779
1780 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1782
1783 registry.modify_policy_whitelist(
1785 admin,
1786 ITIP403Registry::modifyPolicyWhitelistCall {
1787 policyId: recipient_policy,
1788 account: user,
1789 allowed: true,
1790 },
1791 )?;
1792
1793 assert!(registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1795
1796 Ok(())
1797 })
1798 }
1799
1800 #[test]
1801 fn test_compound_policy_is_authorized_transfer() -> eyre::Result<()> {
1802 let admin = Address::random();
1803 let creator = Address::random();
1804 let user = Address::random();
1805
1806 for hardfork in [TempoHardfork::T0, TempoHardfork::T1] {
1807 let mut storage = HashMapStorageProvider::new_with_spec(1, hardfork);
1808
1809 StorageCtx::enter(&mut storage, || {
1810 let mut registry = TIP403Registry::new();
1811
1812 let sender_policy = registry.create_policy(
1814 admin,
1815 ITIP403Registry::createPolicyCall {
1816 admin,
1817 policyType: ITIP403Registry::PolicyType::WHITELIST,
1818 },
1819 )?;
1820 let recipient_policy = registry.create_policy(
1821 admin,
1822 ITIP403Registry::createPolicyCall {
1823 admin,
1824 policyType: ITIP403Registry::PolicyType::WHITELIST,
1825 },
1826 )?;
1827
1828 let compound_id = registry.create_compound_policy(
1830 creator,
1831 ITIP403Registry::createCompoundPolicyCall {
1832 senderPolicyId: sender_policy,
1833 recipientPolicyId: recipient_policy,
1834 mintRecipientPolicyId: 1,
1835 },
1836 )?;
1837
1838 registry.modify_policy_whitelist(
1840 admin,
1841 ITIP403Registry::modifyPolicyWhitelistCall {
1842 policyId: recipient_policy,
1843 account: user,
1844 allowed: true,
1845 },
1846 )?;
1847 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1848
1849 registry.modify_policy_whitelist(
1851 admin,
1852 ITIP403Registry::modifyPolicyWhitelistCall {
1853 policyId: sender_policy,
1854 account: user,
1855 allowed: true,
1856 },
1857 )?;
1858 registry.modify_policy_whitelist(
1859 admin,
1860 ITIP403Registry::modifyPolicyWhitelistCall {
1861 policyId: recipient_policy,
1862 account: user,
1863 allowed: false,
1864 },
1865 )?;
1866 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1867
1868 registry.modify_policy_whitelist(
1870 admin,
1871 ITIP403Registry::modifyPolicyWhitelistCall {
1872 policyId: recipient_policy,
1873 account: user,
1874 allowed: true,
1875 },
1876 )?;
1877 assert!(registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1878
1879 Ok::<_, TempoPrecompileError>(())
1880 })?;
1881 }
1882
1883 Ok(())
1884 }
1885
1886 #[test]
1887 fn test_simple_policy_equivalence() -> eyre::Result<()> {
1888 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1889 let admin = Address::random();
1890 let user = Address::random();
1891 StorageCtx::enter(&mut storage, || {
1892 let mut registry = TIP403Registry::new();
1893
1894 let policy_id = registry.create_policy(
1896 admin,
1897 ITIP403Registry::createPolicyCall {
1898 admin,
1899 policyType: ITIP403Registry::PolicyType::WHITELIST,
1900 },
1901 )?;
1902 registry.modify_policy_whitelist(
1903 admin,
1904 ITIP403Registry::modifyPolicyWhitelistCall {
1905 policyId: policy_id,
1906 account: user,
1907 allowed: true,
1908 },
1909 )?;
1910
1911 let is_authorized = registry.is_authorized_as(policy_id, user, AuthRole::Transfer)?;
1913 let is_sender = registry.is_authorized_as(policy_id, user, AuthRole::Sender)?;
1914 let is_recipient = registry.is_authorized_as(policy_id, user, AuthRole::Recipient)?;
1915 let is_mint_recipient =
1916 registry.is_authorized_as(policy_id, user, AuthRole::MintRecipient)?;
1917
1918 assert!(is_authorized);
1919 assert_eq!(is_authorized, is_sender);
1920 assert_eq!(is_sender, is_recipient);
1921 assert_eq!(is_recipient, is_mint_recipient);
1922
1923 Ok(())
1924 })
1925 }
1926
1927 #[test]
1928 fn test_compound_policy_with_builtin_policies() -> eyre::Result<()> {
1929 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1930 let creator = Address::random();
1931 let user = Address::random();
1932 StorageCtx::enter(&mut storage, || {
1933 let mut registry = TIP403Registry::new();
1934
1935 let compound_id = registry.create_compound_policy(
1940 creator,
1941 ITIP403Registry::createCompoundPolicyCall {
1942 senderPolicyId: 1,
1943 recipientPolicyId: 0,
1944 mintRecipientPolicyId: 1,
1945 },
1946 )?;
1947
1948 assert!(registry.is_authorized_as(compound_id, user, AuthRole::Sender)?);
1950
1951 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Recipient)?);
1953
1954 assert!(registry.is_authorized_as(compound_id, user, AuthRole::MintRecipient)?);
1956
1957 assert!(!registry.is_authorized_as(compound_id, user, AuthRole::Transfer)?);
1959
1960 Ok(())
1961 })
1962 }
1963
1964 #[test]
1965 fn test_vendor_credits_use_case() -> eyre::Result<()> {
1966 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
1967 let admin = Address::random();
1968 let creator = Address::random();
1969 let vendor = Address::random();
1970 let customer = Address::random();
1971 StorageCtx::enter(&mut storage, || {
1972 let mut registry = TIP403Registry::new();
1973
1974 let vendor_whitelist = registry.create_policy(
1976 admin,
1977 ITIP403Registry::createPolicyCall {
1978 admin,
1979 policyType: ITIP403Registry::PolicyType::WHITELIST,
1980 },
1981 )?;
1982 registry.modify_policy_whitelist(
1983 admin,
1984 ITIP403Registry::modifyPolicyWhitelistCall {
1985 policyId: vendor_whitelist,
1986 account: vendor,
1987 allowed: true,
1988 },
1989 )?;
1990
1991 let compound_id = registry.create_compound_policy(
1996 creator,
1997 ITIP403Registry::createCompoundPolicyCall {
1998 senderPolicyId: 1, recipientPolicyId: vendor_whitelist, mintRecipientPolicyId: 1, },
2002 )?;
2003
2004 assert!(registry.is_authorized_as(compound_id, customer, AuthRole::MintRecipient)?);
2006
2007 assert!(registry.is_authorized_as(compound_id, customer, AuthRole::Sender)?);
2009
2010 assert!(registry.is_authorized_as(compound_id, vendor, AuthRole::Recipient)?);
2012 assert!(!registry.is_authorized_as(compound_id, customer, AuthRole::Recipient)?);
2014
2015 Ok(())
2016 })
2017 }
2018
2019 #[test]
2020 fn test_policy_data_rejects_compound_policy_on_pre_t1() -> eyre::Result<()> {
2021 let creator = Address::random();
2022
2023 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
2025 let compound_id = StorageCtx::enter(&mut storage, || {
2026 let mut registry = TIP403Registry::new();
2027 registry.create_compound_policy(
2028 creator,
2029 ITIP403Registry::createCompoundPolicyCall {
2030 senderPolicyId: 1,
2031 recipientPolicyId: 1,
2032 mintRecipientPolicyId: 1,
2033 },
2034 )
2035 })?;
2036
2037 let mut storage = storage.with_spec(TempoHardfork::T0);
2039 StorageCtx::enter(&mut storage, || {
2040 let registry = TIP403Registry::new();
2041
2042 let result = registry.policy_data(ITIP403Registry::policyDataCall {
2043 policyId: compound_id,
2044 });
2045 assert!(result.is_err());
2046 assert_eq!(result.unwrap_err(), TempoPrecompileError::under_overflow());
2047
2048 Ok(())
2049 })
2050 }
2051
2052 #[test]
2053 fn test_create_policy_rejects_non_simple_policy_types() -> eyre::Result<()> {
2054 let admin = Address::random();
2055
2056 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2057 StorageCtx::enter(&mut storage, || {
2058 let mut registry = TIP403Registry::new();
2059
2060 for policy_type in [
2061 ITIP403Registry::PolicyType::COMPOUND,
2062 ITIP403Registry::PolicyType::__Invalid,
2063 ] {
2064 let result = registry.create_policy(
2065 admin,
2066 ITIP403Registry::createPolicyCall {
2067 admin,
2068 policyType: policy_type,
2069 },
2070 );
2071 assert!(matches!(
2072 result.unwrap_err(),
2073 TempoPrecompileError::TIP403RegistryError(
2074 TIP403RegistryError::IncompatiblePolicyType(_)
2075 )
2076 ));
2077 }
2078
2079 Ok(())
2080 })
2081 }
2082
2083 #[test]
2084 fn test_create_policy_with_accounts_rejects_non_simple_policy_types() -> eyre::Result<()> {
2085 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
2086 let admin = Address::random();
2087 let account = Address::random();
2088 StorageCtx::enter(&mut storage, || {
2089 let mut registry = TIP403Registry::new();
2090
2091 for policy_type in [
2092 ITIP403Registry::PolicyType::COMPOUND,
2093 ITIP403Registry::PolicyType::__Invalid,
2094 ] {
2095 let result = registry.create_policy_with_accounts(
2096 admin,
2097 ITIP403Registry::createPolicyWithAccountsCall {
2098 admin,
2099 policyType: policy_type,
2100 accounts: vec![account],
2101 },
2102 );
2103 assert!(matches!(
2104 result.unwrap_err(),
2105 TempoPrecompileError::TIP403RegistryError(
2106 TIP403RegistryError::IncompatiblePolicyType(_)
2107 )
2108 ));
2109 }
2110
2111 Ok(())
2112 })
2113 }
2114
2115 #[test]
2120 fn test_pre_t1_create_policy_with_invalid_type_stores_255() -> eyre::Result<()> {
2121 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
2122 let admin = Address::random();
2123 StorageCtx::enter(&mut storage, || {
2124 let mut registry = TIP403Registry::new();
2125
2126 for policy_type in [
2128 ITIP403Registry::PolicyType::COMPOUND,
2129 ITIP403Registry::PolicyType::__Invalid,
2130 ] {
2131 let policy_id = registry.create_policy(
2132 admin,
2133 ITIP403Registry::createPolicyCall {
2134 admin,
2135 policyType: policy_type,
2136 },
2137 )?;
2138
2139 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall {
2141 policyId: policy_id
2142 })?);
2143
2144 let data = registry.get_policy_data(policy_id)?;
2146 assert_eq!(data.policy_type, 255u8);
2147 }
2148
2149 Ok(())
2150 })
2151 }
2152
2153 #[test]
2154 fn test_pre_t1_create_policy_with_valid_types_stores_correct_value() -> eyre::Result<()> {
2155 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
2156 let admin = Address::random();
2157 StorageCtx::enter(&mut storage, || {
2158 let mut registry = TIP403Registry::new();
2159
2160 let whitelist_id = registry.create_policy(
2162 admin,
2163 ITIP403Registry::createPolicyCall {
2164 admin,
2165 policyType: ITIP403Registry::PolicyType::WHITELIST,
2166 },
2167 )?;
2168 let data = registry.get_policy_data(whitelist_id)?;
2169 assert_eq!(data.policy_type, 0u8);
2170
2171 let blacklist_id = registry.create_policy(
2173 admin,
2174 ITIP403Registry::createPolicyCall {
2175 admin,
2176 policyType: ITIP403Registry::PolicyType::BLACKLIST,
2177 },
2178 )?;
2179 let data = registry.get_policy_data(blacklist_id)?;
2180 assert_eq!(data.policy_type, 1u8);
2181
2182 Ok(())
2183 })
2184 }
2185
2186 #[test]
2187 fn test_pre_t1_create_policy_with_accounts_invalid_type_behavior() -> eyre::Result<()> {
2188 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
2189 let (admin, account) = (Address::random(), Address::random());
2190
2191 StorageCtx::enter(&mut storage, || {
2192 let mut registry = TIP403Registry::new();
2193
2194 for policy_type in [
2196 ITIP403Registry::PolicyType::COMPOUND,
2197 ITIP403Registry::PolicyType::__Invalid,
2198 ] {
2199 let result = registry.create_policy_with_accounts(
2200 admin,
2201 ITIP403Registry::createPolicyWithAccountsCall {
2202 admin,
2203 policyType: policy_type,
2204 accounts: vec![account],
2205 },
2206 );
2207 assert!(matches!(
2208 result.unwrap_err(),
2209 TempoPrecompileError::TIP403RegistryError(
2210 TIP403RegistryError::IncompatiblePolicyType(_)
2211 )
2212 ));
2213 }
2214
2215 let policy_id = registry.create_policy_with_accounts(
2217 admin,
2218 ITIP403Registry::createPolicyWithAccountsCall {
2219 admin,
2220 policyType: ITIP403Registry::PolicyType::__Invalid,
2221 accounts: vec![],
2222 },
2223 )?;
2224 let data = registry.get_policy_data(policy_id)?;
2225 assert_eq!(data.policy_type, 255u8);
2226
2227 Ok(())
2228 })
2229 }
2230
2231 #[test]
2232 fn test_pre_t1_policy_data_reverts_for_any_policy_type_gte_2() -> eyre::Result<()> {
2233 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
2234 let admin = Address::random();
2235 StorageCtx::enter(&mut storage, || {
2236 let mut registry = TIP403Registry::new();
2237
2238 let policy_id = registry.create_policy(
2240 admin,
2241 ITIP403Registry::createPolicyCall {
2242 admin,
2243 policyType: ITIP403Registry::PolicyType::COMPOUND,
2244 },
2245 )?;
2246
2247 let result = registry.policy_data(ITIP403Registry::policyDataCall {
2249 policyId: policy_id,
2250 });
2251 assert!(result.is_err());
2252 assert_eq!(result.unwrap_err(), TempoPrecompileError::under_overflow());
2253
2254 Ok(())
2255 })
2256 }
2257
2258 #[test]
2259 fn test_pre_t1_is_authorized_reverts_for_invalid_policy_type() -> eyre::Result<()> {
2260 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
2261 let admin = Address::random();
2262 let user = Address::random();
2263 StorageCtx::enter(&mut storage, || {
2264 let mut registry = TIP403Registry::new();
2265
2266 let policy_id = registry.create_policy(
2268 admin,
2269 ITIP403Registry::createPolicyCall {
2270 admin,
2271 policyType: ITIP403Registry::PolicyType::COMPOUND,
2272 },
2273 )?;
2274
2275 let result = registry.is_authorized_as(policy_id, user, AuthRole::Transfer);
2277 assert!(result.is_err());
2278 assert_eq!(result.unwrap_err(), TempoPrecompileError::under_overflow());
2279
2280 Ok(())
2281 })
2282 }
2283
2284 #[test]
2285 fn test_pre_t2_to_t2_migration_invalid_policy_still_fails() -> eyre::Result<()> {
2286 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
2288 let admin = Address::random();
2289 let user = Address::random();
2290
2291 let policy_id = StorageCtx::enter(&mut storage, || {
2292 let mut registry = TIP403Registry::new();
2293 registry.create_policy(
2294 admin,
2295 ITIP403Registry::createPolicyCall {
2296 admin,
2297 policyType: ITIP403Registry::PolicyType::COMPOUND,
2298 },
2299 )
2300 })?;
2301
2302 let mut storage = storage.with_spec(TempoHardfork::T2);
2304 StorageCtx::enter(&mut storage, || {
2305 let registry = TIP403Registry::new();
2306
2307 let result = registry.policy_data(ITIP403Registry::policyDataCall {
2309 policyId: policy_id,
2310 });
2311 assert!(result.is_err());
2312 assert_eq!(
2313 result.unwrap_err(),
2314 TIP403RegistryError::invalid_policy_type().into()
2315 );
2316
2317 let result = registry.is_authorized_as(policy_id, user, AuthRole::Transfer);
2319 assert!(result.is_err());
2320 assert_eq!(
2321 result.unwrap_err(),
2322 TIP403RegistryError::invalid_policy_type().into()
2323 );
2324
2325 Ok(())
2326 })
2327 }
2328
2329 #[test]
2330 fn test_t2_compound_policy_rejects_legacy_invalid_255_policy() -> eyre::Result<()> {
2331 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
2333 let admin = Address::random();
2334 let creator = Address::random();
2335
2336 let invalid_policy_id = StorageCtx::enter(&mut storage, || {
2337 let mut registry = TIP403Registry::new();
2338 registry.create_policy(
2339 admin,
2340 ITIP403Registry::createPolicyCall {
2341 admin,
2342 policyType: ITIP403Registry::PolicyType::__Invalid,
2343 },
2344 )
2345 })?;
2346
2347 let mut storage = storage.with_spec(TempoHardfork::T2);
2349 StorageCtx::enter(&mut storage, || {
2350 let mut registry = TIP403Registry::new();
2351
2352 let valid_policy_id = registry.create_policy(
2353 admin,
2354 ITIP403Registry::createPolicyCall {
2355 admin,
2356 policyType: ITIP403Registry::PolicyType::WHITELIST,
2357 },
2358 )?;
2359
2360 let result = registry.create_compound_policy(
2362 creator,
2363 ITIP403Registry::createCompoundPolicyCall {
2364 senderPolicyId: invalid_policy_id,
2365 recipientPolicyId: valid_policy_id,
2366 mintRecipientPolicyId: valid_policy_id,
2367 },
2368 );
2369 assert!(matches!(
2370 result.unwrap_err(),
2371 TempoPrecompileError::TIP403RegistryError(TIP403RegistryError::PolicyNotSimple(_))
2372 ));
2373
2374 Ok(())
2375 })
2376 }
2377
2378 #[test]
2379 fn test_t2_validate_policy_type_returns_correct_u8() -> eyre::Result<()> {
2380 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2381 let admin = Address::random();
2382 StorageCtx::enter(&mut storage, || {
2383 let mut registry = TIP403Registry::new();
2384
2385 let whitelist_id = registry.create_policy(
2387 admin,
2388 ITIP403Registry::createPolicyCall {
2389 admin,
2390 policyType: ITIP403Registry::PolicyType::WHITELIST,
2391 },
2392 )?;
2393 let data = registry.get_policy_data(whitelist_id)?;
2394 assert_eq!(data.policy_type, 0u8);
2395
2396 let blacklist_id = registry.create_policy(
2398 admin,
2399 ITIP403Registry::createPolicyCall {
2400 admin,
2401 policyType: ITIP403Registry::PolicyType::BLACKLIST,
2402 },
2403 )?;
2404 let data = registry.get_policy_data(blacklist_id)?;
2405 assert_eq!(data.policy_type, 1u8);
2406
2407 Ok(())
2408 })
2409 }
2410
2411 #[test]
2412 fn test_is_authorized_simple_inner_errors_on_invalid_policy_type_t2() -> eyre::Result<()> {
2413 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
2417 let admin = Address::random();
2418 let user = Address::random();
2419
2420 let policy_id = StorageCtx::enter(&mut storage, || {
2422 let mut registry = TIP403Registry::new();
2423 registry.create_policy(
2424 admin,
2425 ITIP403Registry::createPolicyCall {
2426 admin,
2427 policyType: ITIP403Registry::PolicyType::COMPOUND,
2428 },
2429 )
2430 })?;
2431
2432 let mut storage = storage.with_spec(TempoHardfork::T2);
2434 StorageCtx::enter(&mut storage, || {
2435 let registry = TIP403Registry::new();
2436
2437 let result = registry.is_authorized_as(policy_id, user, AuthRole::Transfer);
2438 assert_eq!(
2439 result.unwrap_err(),
2440 TIP403RegistryError::invalid_policy_type().into()
2441 );
2442
2443 Ok(())
2444 })
2445 }
2446
2447 #[test]
2448 fn test_pre_t1_whitelist_and_blacklist_work_normally() -> eyre::Result<()> {
2449 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
2450 let admin = Address::random();
2451 let user = Address::random();
2452 StorageCtx::enter(&mut storage, || {
2453 let mut registry = TIP403Registry::new();
2454
2455 let whitelist_id = registry.create_policy(
2457 admin,
2458 ITIP403Registry::createPolicyCall {
2459 admin,
2460 policyType: ITIP403Registry::PolicyType::WHITELIST,
2461 },
2462 )?;
2463
2464 assert!(!registry.is_authorized_as(whitelist_id, user, AuthRole::Transfer)?);
2466
2467 registry.modify_policy_whitelist(
2469 admin,
2470 ITIP403Registry::modifyPolicyWhitelistCall {
2471 policyId: whitelist_id,
2472 account: user,
2473 allowed: true,
2474 },
2475 )?;
2476
2477 assert!(registry.is_authorized_as(whitelist_id, user, AuthRole::Transfer)?);
2479
2480 let blacklist_id = registry.create_policy(
2482 admin,
2483 ITIP403Registry::createPolicyCall {
2484 admin,
2485 policyType: ITIP403Registry::PolicyType::BLACKLIST,
2486 },
2487 )?;
2488
2489 assert!(registry.is_authorized_as(blacklist_id, user, AuthRole::Transfer)?);
2491
2492 registry.modify_policy_blacklist(
2494 admin,
2495 ITIP403Registry::modifyPolicyBlacklistCall {
2496 policyId: blacklist_id,
2497 account: user,
2498 restricted: true,
2499 },
2500 )?;
2501
2502 assert!(!registry.is_authorized_as(blacklist_id, user, AuthRole::Transfer)?);
2504
2505 Ok(())
2506 })
2507 }
2508
2509 #[test]
2510 fn test_pre_t1_create_policy_event_emits_invalid() -> eyre::Result<()> {
2511 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
2512 let admin = Address::random();
2513
2514 StorageCtx::enter(&mut storage, || {
2515 let mut registry = TIP403Registry::new();
2516
2517 let policy_id = registry.create_policy(
2518 admin,
2519 ITIP403Registry::createPolicyCall {
2520 admin,
2521 policyType: ITIP403Registry::PolicyType::COMPOUND,
2522 },
2523 )?;
2524
2525 let data = registry.get_policy_data(policy_id)?;
2526 assert_eq!(data.policy_type, 255u8);
2527
2528 Ok::<_, TempoPrecompileError>(())
2529 })?;
2530
2531 let events = storage.events.get(&TIP403_REGISTRY_ADDRESS).unwrap();
2532 let policy_created_log = Log::new_unchecked(
2533 TIP403_REGISTRY_ADDRESS,
2534 events[0].topics().to_vec(),
2535 events[0].data.clone(),
2536 );
2537 let decoded = ITIP403Registry::PolicyCreated::decode_log(&policy_created_log)?;
2538
2539 assert_eq!(decoded.policyType, ITIP403Registry::PolicyType::__Invalid);
2541
2542 Ok(())
2543 }
2544
2545 #[test]
2546 fn test_t2_create_policy_rejects_invalid_types() -> eyre::Result<()> {
2547 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2548 let admin = Address::random();
2549
2550 StorageCtx::enter(&mut storage, || {
2551 let mut registry = TIP403Registry::new();
2552
2553 for policy_type in [
2554 ITIP403Registry::PolicyType::COMPOUND,
2555 ITIP403Registry::PolicyType::__Invalid,
2556 ] {
2557 let result = registry.create_policy(
2558 admin,
2559 ITIP403Registry::createPolicyCall {
2560 admin,
2561 policyType: policy_type,
2562 },
2563 );
2564 assert!(matches!(
2565 result.unwrap_err(),
2566 TempoPrecompileError::TIP403RegistryError(
2567 TIP403RegistryError::IncompatiblePolicyType(_)
2568 )
2569 ));
2570 }
2571
2572 Ok(())
2573 })
2574 }
2575
2576 #[test]
2577 fn test_t2_create_policy_emits_correct_type() -> eyre::Result<()> {
2578 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2579 let admin = Address::random();
2580
2581 StorageCtx::enter(&mut storage, || {
2582 let mut registry = TIP403Registry::new();
2583
2584 registry.create_policy(
2585 admin,
2586 ITIP403Registry::createPolicyCall {
2587 admin,
2588 policyType: ITIP403Registry::PolicyType::WHITELIST,
2589 },
2590 )?;
2591
2592 registry.create_policy(
2593 admin,
2594 ITIP403Registry::createPolicyCall {
2595 admin,
2596 policyType: ITIP403Registry::PolicyType::BLACKLIST,
2597 },
2598 )?;
2599
2600 Ok::<_, TempoPrecompileError>(())
2601 })?;
2602
2603 let events = storage.events.get(&TIP403_REGISTRY_ADDRESS).unwrap();
2604
2605 let whitelist_log = Log::new_unchecked(
2607 TIP403_REGISTRY_ADDRESS,
2608 events[0].topics().to_vec(),
2609 events[0].data.clone(),
2610 );
2611 let whitelist_decoded = ITIP403Registry::PolicyCreated::decode_log(&whitelist_log)?;
2612 assert_eq!(
2613 whitelist_decoded.policyType,
2614 ITIP403Registry::PolicyType::WHITELIST
2615 );
2616
2617 let blacklist_log = Log::new_unchecked(
2618 TIP403_REGISTRY_ADDRESS,
2619 events[2].topics().to_vec(),
2620 events[2].data.clone(),
2621 );
2622 let blacklist_decoded = ITIP403Registry::PolicyCreated::decode_log(&blacklist_log)?;
2623 assert_eq!(
2624 blacklist_decoded.policyType,
2625 ITIP403Registry::PolicyType::BLACKLIST
2626 );
2627
2628 Ok(())
2629 }
2630
2631 #[test]
2632 fn test_compound_policy_data_error_cases() -> eyre::Result<()> {
2633 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2634 let admin = Address::random();
2635 StorageCtx::enter(&mut storage, || {
2636 let mut registry = TIP403Registry::new();
2637
2638 let result = registry
2640 .compound_policy_data(ITIP403Registry::compoundPolicyDataCall { policyId: 999 });
2641 assert!(matches!(
2642 result.unwrap_err(),
2643 TempoPrecompileError::TIP403RegistryError(TIP403RegistryError::PolicyNotFound(_))
2644 ));
2645
2646 let simple_policy_id = registry.create_policy(
2648 admin,
2649 ITIP403Registry::createPolicyCall {
2650 admin,
2651 policyType: ITIP403Registry::PolicyType::WHITELIST,
2652 },
2653 )?;
2654 let result = registry.compound_policy_data(ITIP403Registry::compoundPolicyDataCall {
2655 policyId: simple_policy_id,
2656 });
2657 assert!(matches!(
2658 result.unwrap_err(),
2659 TempoPrecompileError::TIP403RegistryError(
2660 TIP403RegistryError::IncompatiblePolicyType(_)
2661 )
2662 ));
2663
2664 Ok(())
2665 })
2666 }
2667
2668 #[test]
2669 fn test_invalid_policy_type() -> eyre::Result<()> {
2670 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T0);
2672 let admin = Address::random();
2673 let user = Address::random();
2674
2675 let policy_id = StorageCtx::enter(&mut storage, || {
2676 let mut registry = TIP403Registry::new();
2677 registry.create_policy(
2678 admin,
2679 ITIP403Registry::createPolicyCall {
2680 admin,
2681 policyType: ITIP403Registry::PolicyType::__Invalid,
2682 },
2683 )
2684 })?;
2685
2686 StorageCtx::enter(&mut storage, || {
2688 let registry = TIP403Registry::new();
2689
2690 let result = registry.policy_data(ITIP403Registry::policyDataCall {
2691 policyId: policy_id,
2692 });
2693 assert_eq!(result.unwrap_err(), TempoPrecompileError::under_overflow());
2694
2695 let result = registry.is_authorized_as(policy_id, user, AuthRole::Transfer);
2696 assert_eq!(result.unwrap_err(), TempoPrecompileError::under_overflow());
2697
2698 Ok::<_, TempoPrecompileError>(())
2699 })?;
2700
2701 let mut storage = storage.with_spec(TempoHardfork::T2);
2703 StorageCtx::enter(&mut storage, || {
2704 let registry = TIP403Registry::new();
2705
2706 let result = registry.policy_data(ITIP403Registry::policyDataCall {
2707 policyId: policy_id,
2708 });
2709 assert_eq!(
2710 result.unwrap_err(),
2711 TIP403RegistryError::invalid_policy_type().into()
2712 );
2713
2714 let result = registry.is_authorized_as(policy_id, user, AuthRole::Transfer);
2715 assert_eq!(
2716 result.unwrap_err(),
2717 TIP403RegistryError::invalid_policy_type().into()
2718 );
2719
2720 Ok(())
2721 })
2722 }
2723
2724 #[test]
2725 fn test_initialize_sets_storage_state() -> eyre::Result<()> {
2726 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2727 StorageCtx::enter(&mut storage, || {
2728 let mut registry = TIP403Registry::new();
2729
2730 assert!(!registry.is_initialized()?);
2732
2733 registry.initialize()?;
2735
2736 assert!(registry.is_initialized()?);
2738
2739 let registry2 = TIP403Registry::new();
2741 assert!(registry2.is_initialized()?);
2742
2743 Ok(())
2744 })
2745 }
2746
2747 #[test]
2748 fn test_policy_exists_boundary_at_counter() -> eyre::Result<()> {
2749 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
2750 let admin = Address::random();
2751 StorageCtx::enter(&mut storage, || {
2752 let mut registry = TIP403Registry::new();
2753
2754 let policy_id = registry.create_policy(
2756 admin,
2757 ITIP403Registry::createPolicyCall {
2758 admin,
2759 policyType: ITIP403Registry::PolicyType::WHITELIST,
2760 },
2761 )?;
2762
2763 let counter = registry.policy_id_counter()?;
2765 assert_eq!(counter, 3);
2766
2767 assert!(registry.policy_exists(ITIP403Registry::policyExistsCall {
2769 policyId: policy_id,
2770 })?);
2771
2772 assert!(
2774 !registry.policy_exists(ITIP403Registry::policyExistsCall { policyId: counter })?
2775 );
2776
2777 assert!(!registry.policy_exists(ITIP403Registry::policyExistsCall {
2779 policyId: counter + 1,
2780 })?);
2781
2782 Ok(())
2783 })
2784 }
2785
2786 #[test]
2787 fn test_nonexistent_policy_behavior() -> eyre::Result<()> {
2788 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T1);
2789 let user = Address::random();
2790 let nonexistent_id = 999;
2791
2792 StorageCtx::enter(&mut storage, || -> Result<()> {
2794 let registry = TIP403Registry::new();
2795 let data = registry.get_policy_data(nonexistent_id)?;
2796 assert!(data.is_default());
2797 assert!(!registry.is_authorized_as(nonexistent_id, user, AuthRole::Transfer)?);
2798 Ok(())
2799 })?;
2800
2801 let mut storage = storage.with_spec(TempoHardfork::T2);
2803 StorageCtx::enter(&mut storage, || {
2804 let registry = TIP403Registry::new();
2805 assert_eq!(
2806 registry.get_policy_data(nonexistent_id).unwrap_err(),
2807 TIP403RegistryError::policy_not_found().into()
2808 );
2809 assert_eq!(
2810 registry
2811 .is_authorized_as(nonexistent_id, user, AuthRole::Transfer)
2812 .unwrap_err(),
2813 TIP403RegistryError::policy_not_found().into()
2814 );
2815 Ok(())
2816 })
2817 }
2818
2819 #[test]
2822 fn test_modify_whitelist_rejects_virtual_address() -> eyre::Result<()> {
2823 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T3);
2824 let admin = Address::random();
2825
2826 StorageCtx::enter(&mut storage, || {
2827 let mut registry = TIP403Registry::new();
2828 let policy_id = registry.create_policy(
2829 admin,
2830 ITIP403Registry::createPolicyCall {
2831 admin,
2832 policyType: PolicyType::WHITELIST,
2833 },
2834 )?;
2835
2836 let result = registry.modify_policy_whitelist(
2837 admin,
2838 ITIP403Registry::modifyPolicyWhitelistCall {
2839 policyId: policy_id,
2840 account: Address::new_virtual(MasterId::ZERO, UserTag::ZERO),
2841 allowed: true,
2842 },
2843 );
2844 assert!(matches!(
2845 result.unwrap_err(),
2846 TempoPrecompileError::TIP403RegistryError(
2847 TIP403RegistryError::VirtualAddressNotAllowed(_)
2848 )
2849 ));
2850
2851 Ok(())
2852 })
2853 }
2854
2855 #[test]
2856 fn test_modify_blacklist_rejects_virtual_address() -> eyre::Result<()> {
2857 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T3);
2858 let admin = Address::random();
2859
2860 StorageCtx::enter(&mut storage, || {
2861 let mut registry = TIP403Registry::new();
2862 let policy_id = registry.create_policy(
2863 admin,
2864 ITIP403Registry::createPolicyCall {
2865 admin,
2866 policyType: PolicyType::BLACKLIST,
2867 },
2868 )?;
2869
2870 let result = registry.modify_policy_blacklist(
2871 admin,
2872 ITIP403Registry::modifyPolicyBlacklistCall {
2873 policyId: policy_id,
2874 account: Address::new_virtual(MasterId::ZERO, UserTag::ZERO),
2875 restricted: true,
2876 },
2877 );
2878 assert!(matches!(
2879 result.unwrap_err(),
2880 TempoPrecompileError::TIP403RegistryError(
2881 TIP403RegistryError::VirtualAddressNotAllowed(_)
2882 )
2883 ));
2884
2885 Ok(())
2886 })
2887 }
2888
2889 #[test]
2890 fn test_create_policy_with_accounts_rejects_virtual_address() -> eyre::Result<()> {
2891 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T3);
2892 let admin = Address::random();
2893
2894 StorageCtx::enter(&mut storage, || {
2895 let mut registry = TIP403Registry::new();
2896
2897 let result = registry.create_policy_with_accounts(
2898 admin,
2899 ITIP403Registry::createPolicyWithAccountsCall {
2900 admin,
2901 policyType: PolicyType::WHITELIST,
2902 accounts: vec![
2903 Address::random(),
2904 Address::new_virtual(MasterId::ZERO, UserTag::ZERO),
2905 ],
2906 },
2907 );
2908 assert!(matches!(
2909 result.unwrap_err(),
2910 TempoPrecompileError::TIP403RegistryError(
2911 TIP403RegistryError::VirtualAddressNotAllowed(_)
2912 )
2913 ));
2914
2915 assert_eq!(registry.policy_id_counter()?, 2);
2917
2918 Ok(())
2919 })
2920 }
2921}