1use crate::metadata_address::KeyType::{
2 ContractSpecification, Record, RecordSpecification, Scope, ScopeSpecification, Session,
3};
4use bech32::{Bech32, Hrp};
5use cosmwasm_std::StdError;
6use sha2::{Digest, Sha256};
7use uuid::Uuid;
8
9#[derive(Clone, Copy, Debug, PartialEq)]
10pub enum KeyType {
11 Scope = 0x00,
12 Session = 0x01,
13 Record = 0x02,
14 ContractSpecification = 0x03,
15 ScopeSpecification = 0x04,
16 RecordSpecification = 0x05,
17}
18
19impl KeyType {
20 pub fn to_str(&self) -> &str {
21 match self {
22 Scope => "scope",
23 Session => "session",
24 Record => "record",
25 ContractSpecification => "contractspec",
26 ScopeSpecification => "scopespec",
27 RecordSpecification => "recspec",
28 }
29 }
30}
31
32#[derive(Clone, Debug, PartialEq)]
34pub struct MetadataAddress {
35 pub bech32: String,
36 pub bytes: Vec<u8>,
37 pub key_type: KeyType,
38}
39
40impl MetadataAddress {
41 pub fn contract_specification(
45 contract_specification_uuid: Uuid,
46 ) -> Result<MetadataAddress, StdError> {
47 let key_type_byte = ContractSpecification as u8;
48 let bytes = [key_type_byte]
49 .iter()
50 .cloned()
51 .chain(Self::hex_encode_uuid(contract_specification_uuid))
52 .collect::<Vec<u8>>();
53
54 let addr = Self::encode_bech32(ContractSpecification, &bytes)?;
55
56 Ok(MetadataAddress {
57 bech32: addr,
58 bytes,
59 key_type: ContractSpecification,
60 })
61 }
62
63 pub fn scope_specification(
67 scope_specification_uuid: Uuid,
68 ) -> Result<MetadataAddress, StdError> {
69 let key_type_byte = ScopeSpecification as u8;
70 let bytes = [key_type_byte]
71 .iter()
72 .cloned()
73 .chain(Self::hex_encode_uuid(scope_specification_uuid))
74 .collect::<Vec<u8>>();
75
76 let addr = Self::encode_bech32(ScopeSpecification, &bytes)?;
77
78 Ok(MetadataAddress {
79 bech32: addr,
80 bytes,
81 key_type: ScopeSpecification,
82 })
83 }
84
85 pub fn scope(scope_uuid: Uuid) -> Result<MetadataAddress, StdError> {
89 let key_type_byte = Scope as u8;
90 let bytes = [key_type_byte]
91 .iter()
92 .cloned()
93 .chain(Self::hex_encode_uuid(scope_uuid))
94 .collect::<Vec<u8>>();
95
96 let addr = Self::encode_bech32(Scope, &bytes)?;
97
98 Ok(MetadataAddress {
99 bech32: addr,
100 bytes,
101 key_type: Scope,
102 })
103 }
104
105 pub fn record(scope_uuid: Uuid, record_name: String) -> Result<MetadataAddress, StdError> {
109 let key_type_byte = Record as u8;
110 let bytes = [key_type_byte]
111 .iter()
112 .cloned()
113 .chain(Self::hex_encode_uuid(scope_uuid))
114 .chain(Self::hash_bytes(record_name))
115 .collect::<Vec<u8>>();
116
117 let addr = Self::encode_bech32(Record, &bytes)?;
118
119 Ok(MetadataAddress {
120 bech32: addr,
121 bytes,
122 key_type: Record,
123 })
124 }
125
126 pub fn record_specification(
130 contract_specification_uuid: Uuid,
131 record_specification_name: String,
132 ) -> Result<MetadataAddress, StdError> {
133 let key_type_byte = RecordSpecification as u8;
134 let bytes = [key_type_byte]
135 .iter()
136 .cloned()
137 .chain(Self::hex_encode_uuid(contract_specification_uuid))
138 .chain(Self::hash_bytes(record_specification_name))
139 .collect::<Vec<u8>>();
140
141 let addr = Self::encode_bech32(RecordSpecification, &bytes)?;
142
143 Ok(MetadataAddress {
144 bech32: addr,
145 bytes,
146 key_type: RecordSpecification,
147 })
148 }
149
150 pub fn session(scope_uuid: Uuid, session_uuid: Uuid) -> Result<MetadataAddress, StdError> {
154 let key_type_byte = Session as u8;
155 let bytes = [key_type_byte]
156 .iter()
157 .cloned()
158 .chain(Self::hex_encode_uuid(scope_uuid))
159 .chain(Self::hex_encode_uuid(session_uuid))
160 .collect::<Vec<u8>>();
161
162 let addr = Self::encode_bech32(Session, &bytes)?;
163
164 Ok(MetadataAddress {
165 bech32: addr,
166 bytes,
167 key_type: Session,
168 })
169 }
170
171 pub fn to_bytes(&self) -> Vec<u8> {
172 self.bech32.as_bytes().to_vec()
173 }
174
175 fn hex_encode_uuid(uuid: Uuid) -> Vec<u8> {
176 hex::decode(uuid.simple().encode_lower(&mut Uuid::encode_buffer())).unwrap()
177 }
178
179 fn encode_bech32(key_type: KeyType, bytes: &[u8]) -> Result<String, StdError> {
180 let hrp =
181 Hrp::parse(key_type.to_str()).map_err(|e| StdError::parse_err("Hrp", e.to_string()))?;
182 let encoded = bech32::encode::<Bech32>(hrp, bytes)
183 .map_err(|e| StdError::generic_err(e.to_string()))?;
184
185 Ok(encoded)
186 }
187
188 pub fn hash_bytes(data: String) -> Vec<u8> {
189 let hash = Sha256::digest(data.trim().to_lowercase().as_bytes());
190 hash[0..16].to_vec()
191 }
192}
193
194#[cfg(test)]
195pub mod test {
196 use std::str::FromStr;
197
198 use uuid::Uuid;
199
200 use crate::metadata_address::{KeyType, MetadataAddress};
201
202 #[test]
203 pub fn new_contract_spec() {
204 let meta_addr = MetadataAddress::contract_specification(
205 Uuid::from_str("9fe17f9a-56e1-4158-a8af-450680ac9e60").unwrap(),
206 )
207 .unwrap();
208
209 assert_eq!(
210 meta_addr,
211 MetadataAddress {
212 bech32: "contractspec1qw07zlu62ms5zk9g4azsdq9vnesqy4dtgm".to_string(),
213 bytes: vec![
214 3, 159, 225, 127, 154, 86, 225, 65, 88, 168, 175, 69, 6, 128, 172, 158, 96
215 ],
216 key_type: KeyType::ContractSpecification,
217 }
218 );
219 }
220
221 #[test]
222 pub fn new_record() {
223 let meta_addr = MetadataAddress::record(
224 Uuid::from_str("9fe17f9a-56e1-4158-a8af-450680ac9e60").unwrap(),
225 "nft_record_spec_name".to_string(),
226 )
227 .unwrap();
228
229 assert_eq!(
230 meta_addr,
231 MetadataAddress {
232 bech32: "record1q207zlu62ms5zk9g4azsdq9vneswsu3m8wtqu0zfqu9edf8r7l4jugz3hcl"
233 .to_string(),
234 bytes: vec![
235 2, 159, 225, 127, 154, 86, 225, 65, 88, 168, 175, 69, 6, 128, 172, 158, 96,
236 232, 114, 59, 59, 150, 14, 60, 73, 7, 11, 150, 164, 227, 247, 235, 46
237 ],
238 key_type: KeyType::Record,
239 }
240 );
241 }
242
243 #[test]
244 pub fn new_record_spec() {
245 let meta_addr = MetadataAddress::record_specification(
246 Uuid::from_str("9fe17f9a-56e1-4158-a8af-450680ac9e60").unwrap(),
247 "nft_record_spec_name".to_string(),
248 )
249 .unwrap();
250
251 assert_eq!(
252 meta_addr,
253 MetadataAddress {
254 bech32: "recspec1qk07zlu62ms5zk9g4azsdq9vneswsu3m8wtqu0zfqu9edf8r7l4juadl3c0"
255 .to_string(),
256 bytes: vec![
257 5, 159, 225, 127, 154, 86, 225, 65, 88, 168, 175, 69, 6, 128, 172, 158, 96,
258 232, 114, 59, 59, 150, 14, 60, 73, 7, 11, 150, 164, 227, 247, 235, 46
259 ],
260 key_type: KeyType::RecordSpecification,
261 }
262 );
263 }
264
265 #[test]
266 pub fn new_scope() {
267 let meta_addr =
268 MetadataAddress::scope(Uuid::from_str("9fe17f9a-56e1-4158-a8af-450680ac9e60").unwrap())
269 .unwrap();
270
271 assert_eq!(
272 meta_addr,
273 MetadataAddress {
274 bech32: "scope1qz07zlu62ms5zk9g4azsdq9vnesqg74ssc".to_string(),
275 bytes: vec![
276 0, 159, 225, 127, 154, 86, 225, 65, 88, 168, 175, 69, 6, 128, 172, 158, 96
277 ],
278 key_type: KeyType::Scope,
279 }
280 );
281 }
282
283 #[test]
284 pub fn new_scope_spec() {
285 let meta_addr = MetadataAddress::scope_specification(
286 Uuid::from_str("9fe17f9a-56e1-4158-a8af-450680ac9e60").unwrap(),
287 )
288 .unwrap();
289
290 assert_eq!(
291 meta_addr,
292 MetadataAddress {
293 bech32: "scopespec1qj07zlu62ms5zk9g4azsdq9vnesqxcv7hd".to_string(),
294 bytes: vec![
295 4, 159, 225, 127, 154, 86, 225, 65, 88, 168, 175, 69, 6, 128, 172, 158, 96
296 ],
297 key_type: KeyType::ScopeSpecification,
298 }
299 );
300 }
301
302 #[test]
303 pub fn new_session() {
304 let meta_addr = MetadataAddress::session(
305 Uuid::from_str("9fe17f9a-56e1-4158-a8af-450680ac9e60").unwrap(),
306 Uuid::from_str("9fe17f9a-56e1-4158-a8af-450680ac9e60").unwrap(),
307 )
308 .unwrap();
309
310 assert_eq!(
311 meta_addr,
312 MetadataAddress {
313 bech32: "session1qx07zlu62ms5zk9g4azsdq9vnesflctlnftwzs2c4zh52p5q4j0xqrysdme"
314 .to_string(),
315 bytes: vec![
316 1, 159, 225, 127, 154, 86, 225, 65, 88, 168, 175, 69, 6, 128, 172, 158, 96,
317 159, 225, 127, 154, 86, 225, 65, 88, 168, 175, 69, 6, 128, 172, 158, 96
318 ],
319 key_type: KeyType::Session,
320 }
321 );
322 }
323}