saorsa_core/
address_book.rs1use crate::identity::four_words::FourWordAddress;
11use sha2::{Digest, Sha256};
12use crate::{error::{IdentityError, P2PError}, fwid, Result};
13use once_cell::sync::OnceCell;
14use serde::{Deserialize, Serialize};
15use std::collections::HashMap;
16use std::sync::Arc;
17use tokio::sync::RwLock;
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20struct UserToWordsEntry {
21 user_id: String,
22 four_words: String,
23}
24
25#[derive(Clone)]
26pub struct AddressBook {
27 user_to_words: Arc<RwLock<HashMap<String, String>>>,
28 words_to_user: Arc<RwLock<HashMap<String, String>>>,
29}
30
31impl Default for AddressBook {
32 fn default() -> Self {
33 Self {
34 user_to_words: Arc::new(RwLock::new(HashMap::new())),
35 words_to_user: Arc::new(RwLock::new(HashMap::new())),
36 }
37 }
38}
39
40static GLOBAL_BOOK: OnceCell<AddressBook> = OnceCell::new();
41
42pub fn address_book() -> &'static AddressBook {
43 GLOBAL_BOOK.get_or_init(AddressBook::default)
44}
45
46fn key_user_to_words(user_id: &str) -> String {
47 let k = fwid::compute_key("abw:user", user_id.as_bytes());
48 hex::encode(k.as_bytes())
49}
50
51fn key_words_to_user(words: &str) -> String {
52 let k = fwid::compute_key("abw:words", words.as_bytes());
53 hex::encode(k.as_bytes())
54}
55
56impl AddressBook {
57 pub async fn register(&self, user_id: String, four_words: String) -> Result<()> {
59 let parts: Vec<String> = four_words
61 .split('-')
62 .map(|s| s.to_string())
63 .collect();
64 if parts.len() != 4 || !fwid::fw_check([
65 parts[0].clone(),
66 parts[1].clone(),
67 parts[2].clone(),
68 parts[3].clone(),
69 ]) {
70 return Err(P2PError::Identity(IdentityError::InvalidFourWordAddress(
71 "invalid four-word address format".into(),
72 )));
73 }
74
75 {
76 let mut u2w = self.user_to_words.write().await;
77 u2w.insert(user_id.clone(), four_words.clone());
78 }
79 {
80 let mut w2u = self.words_to_user.write().await;
81 w2u.insert(four_words.clone(), user_id.clone());
82 }
83
84 if let Ok(client) = crate::dht::client::DhtClient::new() {
86 let entry = UserToWordsEntry {
87 user_id: user_id.clone(),
88 four_words: four_words.clone(),
89 };
90 let _ = client.put_object(key_user_to_words(&user_id), &entry).await;
91 let _ = client.put_object(key_words_to_user(&four_words), &entry).await;
92 }
93 Ok(())
94 }
95
96 pub async fn get_words(&self, user_id: &str) -> Result<Option<FourWordAddress>> {
98 if let Some(w) = self.user_to_words.read().await.get(user_id).cloned() {
99 return Ok(Some(FourWordAddress(w)));
100 }
101 if let Ok(client) = crate::dht::client::DhtClient::new()
103 && let Ok(Some(entry)) = client
104 .get_object::<UserToWordsEntry>(key_user_to_words(user_id))
105 .await
106 {
107 {
109 let mut u2w = self.user_to_words.write().await;
110 u2w.insert(entry.user_id.clone(), entry.four_words.clone());
111 }
112 {
113 let mut w2u = self.words_to_user.write().await;
114 w2u.insert(entry.four_words.clone(), entry.user_id.clone());
115 }
116 return Ok(Some(FourWordAddress(entry.four_words)));
117 }
118 Ok(None)
119 }
120
121 pub async fn get_user(&self, words: &str) -> Result<Option<String>> {
123 if let Some(u) = self.words_to_user.read().await.get(words).cloned() {
124 return Ok(Some(u));
125 }
126 if let Ok(client) = crate::dht::client::DhtClient::new()
127 && let Ok(Some(entry)) = client
128 .get_object::<UserToWordsEntry>(key_words_to_user(words))
129 .await
130 {
131 {
133 let mut u2w = self.user_to_words.write().await;
134 u2w.insert(entry.user_id.clone(), entry.four_words.clone());
135 }
136 {
137 let mut w2u = self.words_to_user.write().await;
138 w2u.insert(entry.four_words.clone(), entry.user_id.clone());
139 }
140 return Ok(Some(entry.user_id));
141 }
142
143 let parts: Vec<String> = words.split('-').map(|s| s.to_string()).collect();
146 if parts.len() == 4
147 && crate::fwid::fw_check([
148 parts[0].clone(),
149 parts[1].clone(),
150 parts[2].clone(),
151 parts[3].clone(),
152 ])
153 && let Ok(key) = crate::fwid::fw_to_key([
154 parts[0].clone(),
155 parts[1].clone(),
156 parts[2].clone(),
157 parts[3].clone(),
158 ])
159 && let Ok(pkt) = crate::identity_fetch(key.clone()).await
160 {
161 let mut hasher = Sha256::new();
162 hasher.update(&pkt.pk);
163 let user_id = hex::encode(hasher.finalize());
164 let words_owned = words.to_string();
166 let this = self.clone();
167 let user_id_for_cache = user_id.clone();
168 tokio::spawn(async move {
169 let _ = this.register(user_id_for_cache, words_owned).await;
170 });
171 return Ok(Some(user_id));
172 }
173 Ok(None)
174 }
175}
176
177pub async fn register_user_address(user_id: String, four_words: String) -> Result<()> {
179 address_book().register(user_id, four_words).await
180}
181
182pub async fn get_user_four_words(user_id: &str) -> Result<Option<FourWordAddress>> {
183 address_book().get_words(user_id).await
184}
185
186pub async fn get_user_by_four_words(words: &str) -> Result<Option<String>> {
187 address_book().get_user(words).await
188}