tempo_precompiles/address_registry/
mod.rs1pub mod dispatch;
9
10use crate::{
11 ADDRESS_REGISTRY_ADDRESS,
12 error::Result,
13 storage::{Handler, Mapping},
14};
15use alloy::{
16 primitives::{Address, FixedBytes, keccak256},
17 sol_types::SolValue,
18};
19use tempo_chainspec::hardfork::TempoHardfork;
20pub use tempo_contracts::precompiles::{
21 AddrRegistryError, AddrRegistryEvent, IAddressRegistry, STABLECOIN_DEX_ADDRESS,
22 TIP_FEE_MANAGER_ADDRESS, TIP20_CHANNEL_RESERVE_ADDRESS,
23};
24use tempo_precompiles_macros::{Storable, contract};
25pub use tempo_primitives::{MasterId, TempoAddressExt, UserTag};
26
27pub const IMPLICIT_APPROVAL_LIST: &[Address] = &[
33 TIP_FEE_MANAGER_ADDRESS,
34 STABLECOIN_DEX_ADDRESS,
35 TIP20_CHANNEL_RESERVE_ADDRESS,
36];
37
38pub fn is_implicitly_approved(addr: Address, hardfork: TempoHardfork) -> bool {
42 if !hardfork.is_t5() {
43 return false;
44 }
45 IMPLICIT_APPROVAL_LIST.contains(&addr)
46}
47
48#[contract(addr = ADDRESS_REGISTRY_ADDRESS)]
58pub struct AddressRegistry {
59 data: Mapping<MasterId, RegistryData>,
61}
62
63#[derive(Debug, Clone, Default, Storable)]
65struct RegistryData {
66 master_address: Address,
68 reserved: FixedBytes<11>,
70 ty: u8,
72}
73
74impl RegistryData {
75 fn master_address(&self) -> Option<Address> {
77 match self.master_address {
78 Address::ZERO => None,
79 master => Some(master),
80 }
81 }
82}
83
84impl AddressRegistry {
85 pub fn initialize(&mut self) -> Result<()> {
87 self.__initialize()
88 }
89
90 pub fn register_virtual_master(
102 &mut self,
103 msg_sender: Address,
104 call: IAddressRegistry::registerVirtualMasterCall,
105 ) -> Result<MasterId> {
106 if !msg_sender.is_valid_master() {
108 return Err(AddrRegistryError::invalid_master_address().into());
109 }
110
111 let registration_hash = keccak256((msg_sender, call.salt).abi_encode_packed());
113
114 if registration_hash[0..4] != [0u8; 4] {
116 return Err(AddrRegistryError::proof_of_work_failed().into());
117 }
118
119 let master_id = MasterId::from_slice(®istration_hash[4..8]);
121
122 if let Some(master) = self.data[master_id].read()?.master_address() {
124 return Err(AddrRegistryError::master_id_collision(master).into());
125 }
126
127 self.data[master_id].write(RegistryData {
129 master_address: msg_sender,
130 reserved: FixedBytes::ZERO,
131 ty: 0,
132 })?;
133
134 self.emit_event(AddrRegistryEvent::master_registered(master_id, msg_sender))?;
136
137 Ok(master_id)
138 }
139
140 pub fn get_master(&self, master_id: MasterId) -> Result<Option<Address>> {
144 Ok(self.data[master_id].read()?.master_address())
145 }
146
147 pub fn resolve_recipient(&self, to: Address) -> Result<Address> {
155 if !self.storage.spec().is_t3() {
158 return Ok(to);
159 }
160
161 match to.decode_virtual() {
162 None => Ok(to),
163 Some((master_id, _)) => self
164 .get_master(master_id)?
165 .ok_or(AddrRegistryError::virtual_address_unregistered().into()),
166 }
167 }
168
169 pub fn resolve_virtual_address(&self, addr: Address) -> Result<Address> {
173 match addr.decode_virtual() {
174 None => Ok(Address::ZERO),
175 Some((master_id, _)) => Ok(self.get_master(master_id)?.unwrap_or(Address::ZERO)),
176 }
177 }
178
179 pub fn is_implicitly_approved(&self, addr: Address) -> bool {
182 is_implicitly_approved(addr, self.storage.spec())
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use crate::{
190 error::TempoPrecompileError,
191 storage::{StorageCtx, hashmap::HashMapStorageProvider},
192 test_util::{VIRTUAL_MASTER, VIRTUAL_SALT},
193 };
194 use alloy_primitives::hex_literal::hex;
195 use tempo_chainspec::hardfork::TempoHardfork;
196
197 #[test]
198 fn test_is_implicitly_approved_pre_t5_returns_false() -> eyre::Result<()> {
199 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T4);
200 StorageCtx::enter(&mut storage, || {
201 let registry = AddressRegistry::new();
202 assert!(!registry.is_implicitly_approved(TIP_FEE_MANAGER_ADDRESS));
203 assert!(!registry.is_implicitly_approved(STABLECOIN_DEX_ADDRESS));
204 assert!(!registry.is_implicitly_approved(TIP20_CHANNEL_RESERVE_ADDRESS));
205 assert!(!registry.is_implicitly_approved(Address::random()));
206 Ok(())
207 })
208 }
209
210 #[test]
211 fn test_is_implicitly_approved_t5_lists_initial_set() -> eyre::Result<()> {
212 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T5);
213 StorageCtx::enter(&mut storage, || {
214 let registry = AddressRegistry::new();
215 assert!(registry.is_implicitly_approved(TIP_FEE_MANAGER_ADDRESS));
216 assert!(registry.is_implicitly_approved(STABLECOIN_DEX_ADDRESS));
217 assert!(registry.is_implicitly_approved(TIP20_CHANNEL_RESERVE_ADDRESS));
218 assert!(!registry.is_implicitly_approved(Address::random()));
219 Ok(())
220 })
221 }
222
223 #[test]
224 fn test_register_virtual_master() -> eyre::Result<()> {
225 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
226 let (master, salt) = (VIRTUAL_MASTER, VIRTUAL_SALT.into());
227
228 StorageCtx::enter(&mut storage, || {
229 let mut registry = AddressRegistry::new();
230
231 let master_id = registry.register_virtual_master(
232 master,
233 IAddressRegistry::registerVirtualMasterCall { salt },
234 )?;
235
236 assert_eq!(registry.get_master(master_id)?, Some(master));
237
238 Ok(())
239 })
240 }
241
242 #[test]
243 fn test_register_rejects_bad_pow() -> eyre::Result<()> {
244 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
245 let master = Address::random();
246 let bad_salt = FixedBytes::<32>::ZERO;
247
248 StorageCtx::enter(&mut storage, || {
249 let mut registry = AddressRegistry::new();
250
251 let result = registry.register_virtual_master(
252 master,
253 IAddressRegistry::registerVirtualMasterCall { salt: bad_salt },
254 );
255 assert!(matches!(
256 result.unwrap_err(),
257 TempoPrecompileError::AddrRegistryError(AddrRegistryError::ProofOfWorkFailed(_))
258 ));
259
260 Ok(())
261 })
262 }
263
264 #[test]
265 fn test_register_rejects_zero_address() -> eyre::Result<()> {
266 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
267
268 StorageCtx::enter(&mut storage, || {
269 let mut registry = AddressRegistry::new();
270
271 let result = registry.register_virtual_master(
272 Address::ZERO,
273 IAddressRegistry::registerVirtualMasterCall {
274 salt: FixedBytes::ZERO,
275 },
276 );
277 assert!(matches!(
278 result.unwrap_err(),
279 TempoPrecompileError::AddrRegistryError(AddrRegistryError::InvalidMasterAddress(_))
280 ));
281
282 Ok(())
283 })
284 }
285
286 #[test]
287 fn test_register_rejects_virtual_address_as_master() -> eyre::Result<()> {
288 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
289
290 StorageCtx::enter(&mut storage, || {
291 let mut registry = AddressRegistry::new();
292
293 let result = registry.register_virtual_master(
294 Address::new_virtual(MasterId::ZERO, UserTag::ZERO),
295 IAddressRegistry::registerVirtualMasterCall {
296 salt: FixedBytes::ZERO,
297 },
298 );
299 assert!(matches!(
300 result.unwrap_err(),
301 TempoPrecompileError::AddrRegistryError(AddrRegistryError::InvalidMasterAddress(_))
302 ));
303
304 Ok(())
305 })
306 }
307
308 #[test]
309 fn test_register_rejects_tip20_address_as_master() -> eyre::Result<()> {
310 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
311 let tip20_addr = crate::PATH_USD_ADDRESS;
312
313 StorageCtx::enter(&mut storage, || {
314 let mut registry = AddressRegistry::new();
315
316 let result = registry.register_virtual_master(
317 tip20_addr,
318 IAddressRegistry::registerVirtualMasterCall {
319 salt: FixedBytes::ZERO,
320 },
321 );
322 assert!(matches!(
323 result.unwrap_err(),
324 TempoPrecompileError::AddrRegistryError(AddrRegistryError::InvalidMasterAddress(_))
325 ));
326
327 Ok(())
328 })
329 }
330
331 #[test]
332 fn test_register_duplicate_reverts_with_collision() -> eyre::Result<()> {
333 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
334 let (master, salt) = (VIRTUAL_MASTER, VIRTUAL_SALT.into());
335
336 StorageCtx::enter(&mut storage, || {
337 let mut registry = AddressRegistry::new();
338
339 registry.register_virtual_master(
341 master,
342 IAddressRegistry::registerVirtualMasterCall { salt },
343 )?;
344
345 let result = registry.register_virtual_master(
347 master,
348 IAddressRegistry::registerVirtualMasterCall { salt },
349 );
350 assert!(matches!(
351 result.unwrap_err(),
352 TempoPrecompileError::AddrRegistryError(AddrRegistryError::MasterIdCollision(_))
353 ));
354
355 Ok(())
356 })
357 }
358
359 #[test]
360 fn test_is_virtual_address() {
361 assert!(!Address::random().is_virtual());
362 assert!(Address::new_virtual(MasterId::random(), UserTag::random()).is_virtual());
363 }
364
365 #[test]
366 fn test_decode_virtual_address() {
367 let mid = MasterId::random();
368 let tag = UserTag::random();
369 let addr = Address::new_virtual(mid, tag);
370
371 let (master_id, user_tag) = addr.decode_virtual().unwrap();
372 assert_eq!(master_id, mid);
373 assert_eq!(user_tag, tag);
374
375 assert!(Address::random().decode_virtual().is_none());
376 }
377
378 #[test]
379 fn test_resolve_recipient_non_virtual() -> eyre::Result<()> {
380 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
381 let normal_addr = Address::random();
382
383 StorageCtx::enter(&mut storage, || {
384 let registry = AddressRegistry::new();
385
386 let resolved = registry.resolve_recipient(normal_addr)?;
387 assert_eq!(resolved, normal_addr);
388
389 Ok(())
390 })
391 }
392
393 #[test]
394 fn test_resolve_recipient_virtual_unregistered_reverts() -> eyre::Result<()> {
395 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T3);
396 let virtual_addr = Address::new_virtual(MasterId::ZERO, UserTag::ZERO);
397
398 StorageCtx::enter(&mut storage, || {
399 let registry = AddressRegistry::new();
400
401 let result = registry.resolve_recipient(virtual_addr);
402 assert!(matches!(
403 result.unwrap_err(),
404 TempoPrecompileError::AddrRegistryError(
405 AddrRegistryError::VirtualAddressUnregistered(_)
406 )
407 ));
408
409 Ok(())
410 })
411 }
412
413 #[test]
414 fn test_resolve_recipient_virtual_registered() -> eyre::Result<()> {
415 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T3);
416 let (master, salt) = (VIRTUAL_MASTER, VIRTUAL_SALT.into());
417
418 StorageCtx::enter(&mut storage, || {
419 let mut registry = AddressRegistry::new();
420
421 let master_id = registry.register_virtual_master(
422 master,
423 IAddressRegistry::registerVirtualMasterCall { salt },
424 )?;
425
426 let virtual_addr = Address::new_virtual(master_id, UserTag::new(hex!("010203040506")));
427
428 let resolved = registry.resolve_recipient(virtual_addr)?;
429 assert_eq!(resolved, master);
430
431 Ok(())
432 })
433 }
434
435 #[test]
436 fn test_resolve_virtual_address_view() -> eyre::Result<()> {
437 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T3);
438 let (master, salt) = (VIRTUAL_MASTER, VIRTUAL_SALT.into());
439
440 StorageCtx::enter(&mut storage, || {
441 let mut registry = AddressRegistry::new();
442
443 assert_eq!(
445 registry.resolve_virtual_address(Address::random())?,
446 Address::ZERO
447 );
448
449 let unregistered = Address::new_virtual(MasterId::ZERO, UserTag::ZERO);
451 assert_eq!(
452 registry.resolve_virtual_address(unregistered)?,
453 Address::ZERO
454 );
455
456 let master_id = registry.register_virtual_master(
458 master,
459 IAddressRegistry::registerVirtualMasterCall { salt },
460 )?;
461 let virtual_addr = Address::new_virtual(master_id, UserTag::new(hex!("aabbccddeeff")));
462 assert_eq!(registry.resolve_virtual_address(virtual_addr)?, master);
463
464 Ok(())
465 })
466 }
467
468 #[test]
469 fn test_resolve_recipient_pre_t3_returns_literal() -> eyre::Result<()> {
470 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
471 let virtual_addr = Address::new_virtual(MasterId::ZERO, UserTag::ZERO);
472
473 StorageCtx::enter(&mut storage, || {
474 let registry = AddressRegistry::new();
475 assert_eq!(registry.resolve_recipient(virtual_addr)?, virtual_addr);
476 Ok(())
477 })
478 }
479
480 #[test]
481 fn test_is_valid_master_address() {
482 assert!(!Address::ZERO.is_valid_master());
483 assert!(!Address::new_virtual(MasterId::ZERO, UserTag::ZERO).is_valid_master());
484 assert!(!crate::PATH_USD_ADDRESS.is_valid_master());
485 assert!(Address::repeat_byte(0x42).is_valid_master());
486 }
487}