tempo_precompiles/address_registry/
dispatch.rs1use crate::{
2 Precompile, SelectorSchedule, address_registry::AddressRegistry, charge_input_cost,
3 dispatch_call, mutate, view,
4};
5use alloy::{
6 primitives::Address,
7 sol_types::{SolCall, SolInterface},
8};
9use revm::precompile::PrecompileResult;
10use tempo_chainspec::hardfork::TempoHardfork;
11use tempo_contracts::precompiles::IAddressRegistry::{self, IAddressRegistryCalls};
12use tempo_primitives::{MasterId, TempoAddressExt, UserTag};
13
14const T5_ADDED: &[[u8; 4]] = &[IAddressRegistry::isImplicitlyApprovedCall::SELECTOR];
16
17impl Precompile for AddressRegistry {
18 fn call(&mut self, calldata: &[u8], msg_sender: Address) -> PrecompileResult {
19 if let Some(err) = charge_input_cost(&mut self.storage, calldata) {
20 return err;
21 }
22
23 dispatch_call(
24 calldata,
25 &[SelectorSchedule::new(TempoHardfork::T5).with_added(T5_ADDED)],
26 IAddressRegistryCalls::abi_decode,
27 |call| match call {
28 IAddressRegistryCalls::registerVirtualMaster(call) => {
30 mutate(call, msg_sender, |s, c| self.register_virtual_master(s, c))
31 }
32 IAddressRegistryCalls::getMaster(call) => view(call, |c| {
34 Ok(self.get_master(c.masterId)?.unwrap_or(Address::ZERO))
35 }),
36 IAddressRegistryCalls::resolveRecipient(call) => {
37 view(call, |c| self.resolve_recipient(c.to))
38 }
39 IAddressRegistryCalls::resolveVirtualAddress(call) => {
40 view(call, |c| self.resolve_virtual_address(c.virtualAddr))
41 }
42 IAddressRegistryCalls::isVirtualAddress(call) => {
44 view(call, |c| Ok(c.addr.is_virtual()))
45 }
46 IAddressRegistryCalls::decodeVirtualAddress(call) => view(call, |c| {
47 let (is_virtual, master_id, user_tag) = match c.addr.decode_virtual() {
48 Some((mid, tag)) => (true, mid, tag),
49 None => (false, MasterId::ZERO, UserTag::ZERO),
50 };
51 Ok((is_virtual, master_id, user_tag).into())
52 }),
53 IAddressRegistryCalls::isImplicitlyApproved(call) => {
54 view(call, |c| Ok(self.is_implicitly_approved(c.addr)))
55 }
56 },
57 )
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64 use crate::{
65 address_registry::IAddressRegistry,
66 storage::{StorageCtx, hashmap::HashMapStorageProvider},
67 test_util::{assert_full_coverage, check_selector_coverage},
68 };
69 use alloy::sol_types::{SolCall, SolError, SolValue};
70 use tempo_chainspec::hardfork::TempoHardfork;
71
72 #[test]
73 fn test_selector_coverage() -> eyre::Result<()> {
74 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T5);
75 StorageCtx::enter(&mut storage, || {
76 let mut registry = AddressRegistry::new();
77
78 let unsupported = check_selector_coverage(
79 &mut registry,
80 IAddressRegistryCalls::SELECTORS,
81 "IAddressRegistry",
82 IAddressRegistryCalls::name_by_selector,
83 );
84
85 assert_full_coverage([unsupported]);
86
87 Ok(())
88 })
89 }
90
91 #[test]
92 fn test_is_implicitly_approved_selector_gated_pre_t5() -> eyre::Result<()> {
93 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T4);
95 StorageCtx::enter(&mut storage, || {
96 let mut registry = AddressRegistry::new();
97 let call = IAddressRegistry::isImplicitlyApprovedCall {
98 addr: Address::ZERO,
99 };
100 let result = registry.call(&call.abi_encode(), Address::ZERO)?;
101 assert!(result.is_revert());
102 assert!(
103 tempo_contracts::precompiles::UnknownFunctionSelector::abi_decode(&result.bytes)
104 .is_ok()
105 );
106 Ok(())
107 })
108 }
109
110 #[test]
111 fn test_is_implicitly_approved_precompile_t5() -> eyre::Result<()> {
112 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T5);
113 StorageCtx::enter(&mut storage, || {
114 let mut registry = AddressRegistry::new();
115
116 let call = IAddressRegistry::isImplicitlyApprovedCall {
118 addr: tempo_contracts::precompiles::TIP_FEE_MANAGER_ADDRESS,
119 };
120 let result = registry.call(&call.abi_encode(), Address::ZERO)?;
121 assert!(!result.is_revert());
122 assert!(bool::abi_decode(&result.bytes).unwrap());
123
124 let call = IAddressRegistry::isImplicitlyApprovedCall {
126 addr: Address::random(),
127 };
128 let result = registry.call(&call.abi_encode(), Address::ZERO)?;
129 assert!(!result.is_revert());
130 assert!(!bool::abi_decode(&result.bytes).unwrap());
131
132 Ok(())
133 })
134 }
135
136 #[test]
137 fn test_get_master_precompile() -> eyre::Result<()> {
138 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T3);
139 StorageCtx::enter(&mut storage, || {
140 let mut registry = AddressRegistry::new();
141
142 let call = IAddressRegistry::getMasterCall {
144 masterId: Default::default(),
145 };
146 let result = registry.call(&call.abi_encode(), Address::ZERO)?;
147 assert!(!result.is_revert());
148 let addr = Address::abi_decode(&result.bytes).unwrap();
149 assert_eq!(addr, Address::ZERO);
150
151 Ok(())
152 })
153 }
154
155 #[test]
156 fn test_is_virtual_address_precompile() -> eyre::Result<()> {
157 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T3);
158 StorageCtx::enter(&mut storage, || {
159 let mut registry = AddressRegistry::new();
160
161 let call = IAddressRegistry::isVirtualAddressCall {
163 addr: Address::random(),
164 };
165 let result = registry.call(&call.abi_encode(), Address::ZERO)?;
166 assert!(!bool::abi_decode(&result.bytes).unwrap());
167
168 let mut bytes = [0u8; 20];
170 bytes[4..14].fill(0xFD);
171 let call = IAddressRegistry::isVirtualAddressCall {
172 addr: Address::from(bytes),
173 };
174 let result = registry.call(&call.abi_encode(), Address::ZERO)?;
175 assert!(bool::abi_decode(&result.bytes).unwrap());
176
177 Ok(())
178 })
179 }
180}