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};
19pub use tempo_contracts::precompiles::{AddrRegistryError, AddrRegistryEvent, IAddressRegistry};
20use tempo_precompiles_macros::{Storable, contract};
21pub use tempo_primitives::{MasterId, TempoAddressExt, UserTag};
22
23#[contract(addr = ADDRESS_REGISTRY_ADDRESS)]
33pub struct AddressRegistry {
34 data: Mapping<MasterId, RegistryData>,
36}
37
38#[derive(Debug, Clone, Default, Storable)]
40struct RegistryData {
41 master_address: Address,
43 reserved: FixedBytes<11>,
45 ty: u8,
47}
48
49impl RegistryData {
50 fn master_address(&self) -> Option<Address> {
52 match self.master_address {
53 Address::ZERO => None,
54 master => Some(master),
55 }
56 }
57}
58
59impl AddressRegistry {
60 pub fn initialize(&mut self) -> Result<()> {
62 self.__initialize()
63 }
64
65 pub fn register_virtual_master(
77 &mut self,
78 msg_sender: Address,
79 call: IAddressRegistry::registerVirtualMasterCall,
80 ) -> Result<MasterId> {
81 if !msg_sender.is_valid_master() {
83 return Err(AddrRegistryError::invalid_master_address().into());
84 }
85
86 let registration_hash = keccak256((msg_sender, call.salt).abi_encode_packed());
88
89 if registration_hash[0..4] != [0u8; 4] {
91 return Err(AddrRegistryError::proof_of_work_failed().into());
92 }
93
94 let master_id = MasterId::from_slice(®istration_hash[4..8]);
96
97 if let Some(master) = self.data[master_id].read()?.master_address() {
99 return Err(AddrRegistryError::master_id_collision(master).into());
100 }
101
102 self.data[master_id].write(RegistryData {
104 master_address: msg_sender,
105 reserved: FixedBytes::ZERO,
106 ty: 0,
107 })?;
108
109 self.emit_event(AddrRegistryEvent::MasterRegistered(
111 IAddressRegistry::MasterRegistered {
112 masterId: master_id,
113 masterAddress: msg_sender,
114 },
115 ))?;
116
117 Ok(master_id)
118 }
119
120 pub fn get_master(&self, master_id: MasterId) -> Result<Option<Address>> {
124 Ok(self.data[master_id].read()?.master_address())
125 }
126
127 pub fn resolve_recipient(&self, to: Address) -> Result<Address> {
135 if !self.storage.spec().is_t3() {
138 return Ok(to);
139 }
140
141 match to.decode_virtual() {
142 None => Ok(to),
143 Some((master_id, _)) => self
144 .get_master(master_id)?
145 .ok_or(AddrRegistryError::virtual_address_unregistered().into()),
146 }
147 }
148
149 pub fn resolve_virtual_address(&self, addr: Address) -> Result<Address> {
153 match addr.decode_virtual() {
154 None => Ok(Address::ZERO),
155 Some((master_id, _)) => Ok(self.get_master(master_id)?.unwrap_or(Address::ZERO)),
156 }
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::{
164 error::TempoPrecompileError,
165 storage::{StorageCtx, hashmap::HashMapStorageProvider},
166 test_util::{VIRTUAL_MASTER, VIRTUAL_SALT},
167 };
168 use alloy_primitives::hex_literal::hex;
169 use tempo_chainspec::hardfork::TempoHardfork;
170
171 #[test]
172 fn test_register_virtual_master() -> eyre::Result<()> {
173 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
174 let (master, salt) = (VIRTUAL_MASTER, VIRTUAL_SALT.into());
175
176 StorageCtx::enter(&mut storage, || {
177 let mut registry = AddressRegistry::new();
178
179 let master_id = registry.register_virtual_master(
180 master,
181 IAddressRegistry::registerVirtualMasterCall { salt },
182 )?;
183
184 assert_eq!(registry.get_master(master_id)?, Some(master));
185
186 Ok(())
187 })
188 }
189
190 #[test]
191 fn test_register_rejects_bad_pow() -> eyre::Result<()> {
192 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
193 let master = Address::random();
194 let bad_salt = FixedBytes::<32>::ZERO;
195
196 StorageCtx::enter(&mut storage, || {
197 let mut registry = AddressRegistry::new();
198
199 let result = registry.register_virtual_master(
200 master,
201 IAddressRegistry::registerVirtualMasterCall { salt: bad_salt },
202 );
203 assert!(matches!(
204 result.unwrap_err(),
205 TempoPrecompileError::AddrRegistryError(AddrRegistryError::ProofOfWorkFailed(_))
206 ));
207
208 Ok(())
209 })
210 }
211
212 #[test]
213 fn test_register_rejects_zero_address() -> eyre::Result<()> {
214 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
215
216 StorageCtx::enter(&mut storage, || {
217 let mut registry = AddressRegistry::new();
218
219 let result = registry.register_virtual_master(
220 Address::ZERO,
221 IAddressRegistry::registerVirtualMasterCall {
222 salt: FixedBytes::ZERO,
223 },
224 );
225 assert!(matches!(
226 result.unwrap_err(),
227 TempoPrecompileError::AddrRegistryError(AddrRegistryError::InvalidMasterAddress(_))
228 ));
229
230 Ok(())
231 })
232 }
233
234 #[test]
235 fn test_register_rejects_virtual_address_as_master() -> eyre::Result<()> {
236 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
237
238 StorageCtx::enter(&mut storage, || {
239 let mut registry = AddressRegistry::new();
240
241 let result = registry.register_virtual_master(
242 Address::new_virtual(MasterId::ZERO, UserTag::ZERO),
243 IAddressRegistry::registerVirtualMasterCall {
244 salt: FixedBytes::ZERO,
245 },
246 );
247 assert!(matches!(
248 result.unwrap_err(),
249 TempoPrecompileError::AddrRegistryError(AddrRegistryError::InvalidMasterAddress(_))
250 ));
251
252 Ok(())
253 })
254 }
255
256 #[test]
257 fn test_register_rejects_tip20_address_as_master() -> eyre::Result<()> {
258 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
259 let tip20_addr = crate::PATH_USD_ADDRESS;
260
261 StorageCtx::enter(&mut storage, || {
262 let mut registry = AddressRegistry::new();
263
264 let result = registry.register_virtual_master(
265 tip20_addr,
266 IAddressRegistry::registerVirtualMasterCall {
267 salt: FixedBytes::ZERO,
268 },
269 );
270 assert!(matches!(
271 result.unwrap_err(),
272 TempoPrecompileError::AddrRegistryError(AddrRegistryError::InvalidMasterAddress(_))
273 ));
274
275 Ok(())
276 })
277 }
278
279 #[test]
280 fn test_register_duplicate_reverts_with_collision() -> eyre::Result<()> {
281 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
282 let (master, salt) = (VIRTUAL_MASTER, VIRTUAL_SALT.into());
283
284 StorageCtx::enter(&mut storage, || {
285 let mut registry = AddressRegistry::new();
286
287 registry.register_virtual_master(
289 master,
290 IAddressRegistry::registerVirtualMasterCall { salt },
291 )?;
292
293 let result = registry.register_virtual_master(
295 master,
296 IAddressRegistry::registerVirtualMasterCall { salt },
297 );
298 assert!(matches!(
299 result.unwrap_err(),
300 TempoPrecompileError::AddrRegistryError(AddrRegistryError::MasterIdCollision(_))
301 ));
302
303 Ok(())
304 })
305 }
306
307 #[test]
308 fn test_is_virtual_address() {
309 assert!(!Address::random().is_virtual());
310 assert!(Address::new_virtual(MasterId::random(), UserTag::random()).is_virtual());
311 }
312
313 #[test]
314 fn test_decode_virtual_address() {
315 let mid = MasterId::random();
316 let tag = UserTag::random();
317 let addr = Address::new_virtual(mid, tag);
318
319 let (master_id, user_tag) = addr.decode_virtual().unwrap();
320 assert_eq!(master_id, mid);
321 assert_eq!(user_tag, tag);
322
323 assert!(Address::random().decode_virtual().is_none());
324 }
325
326 #[test]
327 fn test_resolve_recipient_non_virtual() -> eyre::Result<()> {
328 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
329 let normal_addr = Address::random();
330
331 StorageCtx::enter(&mut storage, || {
332 let registry = AddressRegistry::new();
333
334 let resolved = registry.resolve_recipient(normal_addr)?;
335 assert_eq!(resolved, normal_addr);
336
337 Ok(())
338 })
339 }
340
341 #[test]
342 fn test_resolve_recipient_virtual_unregistered_reverts() -> eyre::Result<()> {
343 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T3);
344 let virtual_addr = Address::new_virtual(MasterId::ZERO, UserTag::ZERO);
345
346 StorageCtx::enter(&mut storage, || {
347 let registry = AddressRegistry::new();
348
349 let result = registry.resolve_recipient(virtual_addr);
350 assert!(matches!(
351 result.unwrap_err(),
352 TempoPrecompileError::AddrRegistryError(
353 AddrRegistryError::VirtualAddressUnregistered(_)
354 )
355 ));
356
357 Ok(())
358 })
359 }
360
361 #[test]
362 fn test_resolve_recipient_virtual_registered() -> eyre::Result<()> {
363 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T3);
364 let (master, salt) = (VIRTUAL_MASTER, VIRTUAL_SALT.into());
365
366 StorageCtx::enter(&mut storage, || {
367 let mut registry = AddressRegistry::new();
368
369 let master_id = registry.register_virtual_master(
370 master,
371 IAddressRegistry::registerVirtualMasterCall { salt },
372 )?;
373
374 let virtual_addr = Address::new_virtual(master_id, UserTag::new(hex!("010203040506")));
375
376 let resolved = registry.resolve_recipient(virtual_addr)?;
377 assert_eq!(resolved, master);
378
379 Ok(())
380 })
381 }
382
383 #[test]
384 fn test_resolve_virtual_address_view() -> eyre::Result<()> {
385 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T3);
386 let (master, salt) = (VIRTUAL_MASTER, VIRTUAL_SALT.into());
387
388 StorageCtx::enter(&mut storage, || {
389 let mut registry = AddressRegistry::new();
390
391 assert_eq!(
393 registry.resolve_virtual_address(Address::random())?,
394 Address::ZERO
395 );
396
397 let unregistered = Address::new_virtual(MasterId::ZERO, UserTag::ZERO);
399 assert_eq!(
400 registry.resolve_virtual_address(unregistered)?,
401 Address::ZERO
402 );
403
404 let master_id = registry.register_virtual_master(
406 master,
407 IAddressRegistry::registerVirtualMasterCall { salt },
408 )?;
409 let virtual_addr = Address::new_virtual(master_id, UserTag::new(hex!("aabbccddeeff")));
410 assert_eq!(registry.resolve_virtual_address(virtual_addr)?, master);
411
412 Ok(())
413 })
414 }
415
416 #[test]
417 fn test_resolve_recipient_pre_t3_returns_literal() -> eyre::Result<()> {
418 let mut storage = HashMapStorageProvider::new_with_spec(1, TempoHardfork::T2);
419 let virtual_addr = Address::new_virtual(MasterId::ZERO, UserTag::ZERO);
420
421 StorageCtx::enter(&mut storage, || {
422 let registry = AddressRegistry::new();
423 assert_eq!(registry.resolve_recipient(virtual_addr)?, virtual_addr);
424 Ok(())
425 })
426 }
427
428 #[test]
429 fn test_is_valid_master_address() {
430 assert!(!Address::ZERO.is_valid_master());
431 assert!(!Address::new_virtual(MasterId::ZERO, UserTag::ZERO).is_valid_master());
432 assert!(!crate::PATH_USD_ADDRESS.is_valid_master());
433 assert!(Address::repeat_byte(0x42).is_valid_master());
434 }
435}