1use std::{
5 marker::PhantomData,
6 time::{Duration, SystemTime, UNIX_EPOCH},
7};
8
9use iota_types::{
10 base_types::{IotaAddress, ObjectID},
11 collection_types::VecMap,
12 dynamic_field::Field,
13 id::ID,
14 object::{MoveObject, Object},
15};
16use serde::{Deserialize, Serialize};
17
18use crate::{
19 constants::IOTA_NAMES_LEAF_EXPIRATION_TIMESTAMP, domain::Domain, error::IotaNamesError,
20};
21
22#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
24pub struct Table<K, V> {
25 pub id: ObjectID,
26 pub size: u64,
27
28 #[serde(skip)]
30 _key: PhantomData<K>,
31 #[serde(skip)]
32 _value: PhantomData<V>,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct Registry {
37 registry: Table<Domain, NameRecord>,
40 reverse_registry: Table<IotaAddress, Domain>,
43}
44
45#[derive(Debug, Serialize, Deserialize)]
46pub struct RegistryEntry {
47 pub id: ObjectID,
48 pub domain: Domain,
49 pub name_record: NameRecord,
50}
51
52#[derive(Debug, Serialize, Deserialize)]
53pub struct ReverseRegistryEntry {
54 pub id: ObjectID,
55 pub address: IotaAddress,
56 pub domain: Domain,
57}
58
59#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
61pub struct NameRecord {
62 pub nft_id: ID,
70 pub expiration_timestamp_ms: u64,
72 pub target_address: Option<IotaAddress>,
74 pub data: VecMap<String, String>,
76}
77
78impl TryFrom<Object> for NameRecord {
79 type Error = IotaNamesError;
80
81 fn try_from(object: Object) -> Result<Self, IotaNamesError> {
82 object
83 .to_rust::<Field<Domain, Self>>()
84 .map(|record| record.value)
85 .ok_or_else(|| IotaNamesError::MalformedObject(object.id()))
86 }
87}
88
89impl TryFrom<MoveObject> for NameRecord {
90 type Error = IotaNamesError;
91
92 fn try_from(object: MoveObject) -> Result<Self, IotaNamesError> {
93 object
94 .to_rust::<Field<Domain, Self>>()
95 .map(|record| record.value)
96 .ok_or_else(|| IotaNamesError::MalformedObject(object.id()))
97 }
98}
99
100impl NameRecord {
101 pub fn is_leaf_record(&self) -> bool {
104 self.expiration_timestamp_ms == IOTA_NAMES_LEAF_EXPIRATION_TIMESTAMP
105 }
106
107 pub fn is_valid_leaf_parent(&self, child: &NameRecord) -> bool {
111 self.nft_id == child.nft_id
112 }
113
114 pub fn is_node_expired(&self, checkpoint_timestamp_ms: u64) -> bool {
117 self.expiration_timestamp_ms < checkpoint_timestamp_ms
118 }
119
120 pub fn expiration_time(&self) -> SystemTime {
122 UNIX_EPOCH + Duration::from_millis(self.expiration_timestamp_ms)
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn expirations() {
132 let system_time: u64 = 100;
133
134 let mut name = NameRecord {
135 nft_id: iota_types::id::ID::new(ObjectID::random()),
136 data: VecMap { contents: vec![] },
137 target_address: Some(IotaAddress::random_for_testing_only()),
138 expiration_timestamp_ms: system_time + 10,
139 };
140
141 assert!(!name.is_node_expired(system_time));
142
143 name.expiration_timestamp_ms = system_time - 10;
144
145 assert!(name.is_node_expired(system_time));
146 }
147}