upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2023-11-01 00:00:00 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2023-11-01 00:00:00 +0000
commit497bf71910f0f224ce66b154d58a228095a40c0a (patch)
treea3d55f0581a27465ea9ab2fd03956881ee13193a /src
parente237328ec611a5891586530c1d3cb26c16c1093b (diff)
feat(login) fetch from discovered write relays
immediately request metadata and relay list from any newly discovered user write relays
Diffstat (limited to 'src')
-rw-r--r--src/client.rs31
-rw-r--r--src/key_handling/users.rs320
2 files changed, 248 insertions, 103 deletions
diff --git a/src/client.rs b/src/client.rs
index 5ddf742..929f19a 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -101,14 +101,7 @@ impl Connect for Client {
101 filters.clone(), 101 filters.clone(),
102 ) 102 )
103 }) 103 })
104 .map(|(relay, filters)| { 104 .map(|(relay, filters)| get_events_of(relay, filters)),
105 relay.get_events_of(
106 filters,
107 // 20 is nostr_sdk default
108 std::time::Duration::from_secs(20),
109 nostr_sdk::FilterOptions::ExitOnEOSE,
110 )
111 }),
112 ) 105 )
113 .await; 106 .await;
114 107
@@ -116,6 +109,24 @@ impl Connect for Client {
116 } 109 }
117} 110}
118 111
112async fn get_events_of(
113 relay: &nostr_sdk::Relay,
114 filters: Vec<nostr::Filter>,
115) -> Result<Vec<Event>> {
116 if !relay.is_connected().await {
117 relay.connect(true).await;
118 }
119 relay
120 .get_events_of(
121 filters,
122 // 20 is nostr_sdk default
123 std::time::Duration::from_secs(20),
124 nostr_sdk::FilterOptions::ExitOnEOSE,
125 )
126 .await
127 .context("failed to get events from relay")
128}
129
119#[derive(Default)] 130#[derive(Default)]
120pub struct Params { 131pub struct Params {
121 pub keys: Option<nostr::Keys>, 132 pub keys: Option<nostr::Keys>,
@@ -133,9 +144,7 @@ impl Params {
133 } 144 }
134} 145}
135 146
136fn get_dedup_events( 147fn get_dedup_events(relay_results: Vec<Result<Vec<nostr::Event>>>) -> Vec<Event> {
137 relay_results: Vec<Result<Vec<nostr::Event>, nostr_sdk::relay::Error>>,
138) -> Vec<Event> {
139 let mut dedup_events: Vec<Event> = vec![]; 148 let mut dedup_events: Vec<Event> = vec![];
140 for events in relay_results.into_iter().flatten() { 149 for events in relay_results.into_iter().flatten() {
141 for event in events { 150 for event in events {
diff --git a/src/key_handling/users.rs b/src/key_handling/users.rs
index 91519bc..a486296 100644
--- a/src/key_handling/users.rs
+++ b/src/key_handling/users.rs
@@ -143,7 +143,8 @@ impl UserManagement for UserManager {
143 .clone()) 143 .clone())
144 } 144 }
145 /// get UserRef fetching most recent user relays and metadata infomation 145 /// get UserRef fetching most recent user relays and metadata infomation
146 /// from relays 146 /// from
147 #[allow(clippy::too_many_lines)]
147 async fn get_user( 148 async fn get_user(
148 &self, 149 &self,
149 #[cfg(test)] client: &MockConnect, 150 #[cfg(test)] client: &MockConnect,
@@ -155,103 +156,127 @@ impl UserManagement for UserManager {
155 .config_manager 156 .config_manager
156 .load() 157 .load()
157 .context("failed to load application config")?; 158 .context("failed to load application config")?;
158 let user_ref = cfg 159 let mut user_ref = cfg
159 .users 160 .users
160 .iter() 161 .iter()
161 .find(|u| u.public_key.eq(public_key)) 162 .find(|u| u.public_key.eq(public_key))
162 .context(format!("pubkey isn't a current user: {public_key}"))?; 163 .context(format!("pubkey isn't a current user: {public_key}"))?
164 .clone();
163 // return cache if last fetched was within X minutes 165 // return cache if last fetched was within X minutes
164 if !unix_timestamp_after_now_plus_secs( 166 if !unix_timestamp_after_now_plus_secs(
165 user_ref.last_checked, 167 user_ref.last_checked,
166 use_cache_unless_checked_more_than_x_secs_ago, 168 use_cache_unless_checked_more_than_x_secs_ago,
167 ) { 169 ) {
168 return Ok(user_ref.clone()); 170 return Ok(user_ref);
169 } 171 }
170 let events: Vec<Event> = match client 172
171 .get_events( 173 let mut relays_to_search = if user_ref.relays.write().is_empty() {
172 if user_ref.relays.write().is_empty() { 174 client.get_fallback_relays().clone()
173 client.get_fallback_relays().clone() 175 } else {
174 } else { 176 user_ref.relays.write()
175 user_ref.relays.write()
176 },
177 vec![
178 nostr::Filter::default()
179 .author(public_key.to_string())
180 .since(nostr::Timestamp::from(user_ref.metadata.created_at + 1))
181 .kind(Kind::Metadata),
182 nostr::Filter::default()
183 .author(public_key.to_string())
184 .since(nostr::Timestamp::from(user_ref.relays.created_at + 1))
185 .kind(Kind::RelayList),
186 ],
187 )
188 .await
189 {
190 Ok(events) => events,
191 Err(_) => {
192 // TODO error reporting
193 return Ok(user_ref.clone());
194 }
195 }; 177 };
196 178
197 let mut user_ref = user_ref.clone(); 179 let mut relays_searched: Vec<String> = vec![];
198 180
199 user_ref.last_checked = SystemTime::now() 181 loop {
200 .duration_since(SystemTime::UNIX_EPOCH) 182 for r in &relays_to_search {
201 .context("system time should be after the year 1970")? 183 if !relays_searched.iter().any(|sr| r.eq(sr)) {
202 .as_secs(); 184 relays_searched.push(r.clone());
203 185 }
204 if let Some(new_metadata_event) = events
205 .iter()
206 .filter(|e| e.kind.eq(&nostr::Kind::Metadata) && e.pubkey.eq(public_key))
207 .max_by_key(|e| e.created_at)
208 {
209 if new_metadata_event.created_at.as_u64() > user_ref.metadata.created_at {
210 let metadata = nostr::Metadata::from_json(new_metadata_event.content.clone())
211 .context("metadata cannot be found in kind 0 event content")?;
212 user_ref.metadata = UserMetadata {
213 name: metadata
214 .name
215 .context("user metadata should always have name")?,
216 created_at: new_metadata_event.created_at.as_u64(),
217 };
218 } 186 }
219 };
220 187
221 if let Some(new_relays_event) = events 188 let events: Vec<Event> = match client
222 .iter() 189 .get_events(
223 .filter(|e| e.kind.eq(&nostr::Kind::RelayList) && e.pubkey.eq(public_key)) 190 relays_to_search,
224 .max_by_key(|e| e.created_at) 191 vec![
225 { 192 nostr::Filter::default()
226 if new_relays_event.created_at.as_u64() > user_ref.relays.created_at { 193 .author(public_key.to_string())
227 user_ref.relays = UserRelays { 194 .since(nostr::Timestamp::from(user_ref.metadata.created_at + 1))
228 relays: new_relays_event 195 .kind(Kind::Metadata),
229 .tags 196 nostr::Filter::default()
197 .author(public_key.to_string())
198 .since(nostr::Timestamp::from(user_ref.relays.created_at + 1))
199 .kind(Kind::RelayList),
200 ],
201 )
202 .await
203 {
204 Ok(events) => events,
205 Err(_) => {
206 return Ok(user_ref.clone());
207 }
208 };
209
210 user_ref.last_checked = SystemTime::now()
211 .duration_since(SystemTime::UNIX_EPOCH)
212 .context("system time should be after the year 1970")?
213 .as_secs();
214
215 if let Some(new_metadata_event) = events
216 .iter()
217 .filter(|e| e.kind.eq(&nostr::Kind::Metadata) && e.pubkey.eq(public_key))
218 .max_by_key(|e| e.created_at)
219 {
220 if new_metadata_event.created_at.as_u64() > user_ref.metadata.created_at {
221 let metadata = nostr::Metadata::from_json(new_metadata_event.content.clone())
222 .context("metadata cannot be found in kind 0 event content")?;
223 user_ref.metadata = UserMetadata {
224 name: metadata
225 .name
226 .context("user metadata should always have name")?,
227 created_at: new_metadata_event.created_at.as_u64(),
228 };
229 }
230 };
231
232 if let Some(new_relays_event) = events
233 .iter()
234 .filter(|e| e.kind.eq(&nostr::Kind::RelayList) && e.pubkey.eq(public_key))
235 .max_by_key(|e| e.created_at)
236 {
237 if new_relays_event.created_at.as_u64() > user_ref.relays.created_at {
238 let new_relay_list = UserRelays {
239 relays: new_relays_event
240 .tags
241 .iter()
242 .filter(|t| t.kind().eq(&nostr::TagKind::R))
243 .map(|t| UserRelayRef {
244 url: t.as_vec()[1].clone(),
245 read: t.as_vec().len() == 2 || t.as_vec()[2].eq("read"),
246 write: t.as_vec().len() == 2 || t.as_vec()[2].eq("write"),
247 })
248 .collect(),
249 created_at: new_relays_event.created_at.as_u64(),
250 };
251 let new_relays: Vec<String> = new_relay_list
252 .write()
230 .iter() 253 .iter()
231 .filter(|t| t.kind().eq(&nostr::TagKind::R)) 254 .filter(|r| !relays_searched.iter().any(|or| r.eq(&or)))
232 .map(|t| UserRelayRef { 255 .map(std::clone::Clone::clone)
233 url: t.as_vec()[1].clone(), 256 .collect();
234 read: t.as_vec().len() == 2 || t.as_vec()[2].eq("read"), 257 user_ref.relays = new_relay_list;
235 write: t.as_vec().len() == 2 || t.as_vec()[2].eq("write"), 258
236 }) 259 if !new_relays.is_empty() {
237 .collect(), 260 relays_to_search = new_relays;
238 created_at: new_relays_event.created_at.as_u64(), 261 continue;
239 }; 262 }
240 } 263 }
241 }; 264 };
242 265
243 // remove any duplicate entries for key before adding it to config 266 // remove any duplicate entries for key before adding it to config
244 let mut cfg = self.config_manager.load().context("failed to load application config to find and remove any old versions of the user's encrypted key")?; 267 let mut cfg = self.config_manager.load().context("failed to load application config to find and remove any old versions of the user's encrypted key")?;
245 cfg.users = cfg 268 cfg.users = cfg
246 .users 269 .users
247 .clone() 270 .clone()
248 .into_iter() 271 .into_iter()
249 .filter(|r| !r.public_key.eq(public_key)) 272 .filter(|r| !r.public_key.eq(public_key))
250 .collect(); 273 .collect();
251 cfg.users.push(user_ref.clone()); 274 cfg.users.push(user_ref.clone());
252 self.config_manager 275 self.config_manager
253 .save(&cfg) 276 .save(&cfg)
254 .context("failed to save application configuration with new user details in")?; 277 .context("failed to save application configuration with new user details in")?;
278 break;
279 }
255 Ok(user_ref) 280 Ok(user_ref)
256 } 281 }
257} 282}
@@ -611,23 +636,33 @@ mod tests {
611 .clone() 636 .clone()
612 } 637 }
613 638
639 fn expected_userrelayrefs_write1() -> UserRelayRef {
640 UserRelayRef {
641 url: "wss://fredswrite1.relay".into(),
642 read: false,
643 write: true,
644 }
645 .clone()
646 }
647
648 fn expected_userrelayrefs_read_write1() -> UserRelayRef {
649 UserRelayRef {
650 url: "wss://fredsreadwrite.relay".into(),
651 read: true,
652 write: true,
653 }
654 .clone()
655 }
656
614 fn expected_userrelayrefs() -> Vec<UserRelayRef> { 657 fn expected_userrelayrefs() -> Vec<UserRelayRef> {
615 vec![ 658 vec![
616 UserRelayRef { 659 expected_userrelayrefs_write1(),
617 url: "wss://fredswrite1.relay".into(),
618 read: false,
619 write: true,
620 },
621 UserRelayRef { 660 UserRelayRef {
622 url: "wss://fredsread1.relay".into(), 661 url: "wss://fredsread1.relay".into(),
623 read: true, 662 read: true,
624 write: false, 663 write: false,
625 }, 664 },
626 UserRelayRef { 665 expected_userrelayrefs_read_write1(),
627 url: "wss://fredsreadwrite.relay".into(),
628 read: true,
629 write: true,
630 },
631 ] 666 ]
632 } 667 }
633 668
@@ -958,6 +993,107 @@ mod tests {
958 ))?; 993 ))?;
959 Ok(()) 994 Ok(())
960 } 995 }
996
997 mod fetches_from_new_relays_discovered_in_incoming_relay_list {
998 use super::*;
999
1000 #[test]
1001 fn when_all_relays_in_list_are_new_finds_name() -> Result<()> {
1002 let mut m = MockUserManager::default();
1003 let mut client = generate_mock_client();
1004 m.config_manager.expect_load().returning(|| {
1005 Ok(MyConfig {
1006 users: vec![UserRef {
1007 relays: UserRelays {
1008 relays: vec![],
1009 created_at: 0,
1010 },
1011 ..generate_standard_config().users[0].clone()
1012 }],
1013 ..generate_standard_config()
1014 })
1015 });
1016 m.config_manager.expect_save().returning(|_| Ok(()));
1017 client
1018 .expect_get_events()
1019 .times(2)
1020 .withf(move |relays, _filters| {
1021 fallback_relays().eq(relays)
1022 || UserRelays {
1023 relays: expected_userrelayrefs(),
1024 created_at: 0,
1025 }
1026 .write()
1027 .eq(relays)
1028 })
1029 .returning(|relays, _| {
1030 if fallback_relays().eq(&relays) {
1031 Ok(vec![generate_relaylist_event()])
1032 } else if (UserRelays {
1033 relays: expected_userrelayrefs(),
1034 created_at: 0,
1035 })
1036 .write()
1037 .eq(&relays)
1038 {
1039 Ok(vec![generate_test_key_1_metadata_event("fred")])
1040 } else {
1041 Ok(vec![])
1042 }
1043 });
1044
1045 let res = futures::executor::block_on(m.get_user(
1046 &client,
1047 &TEST_KEY_1_KEYS.public_key(),
1048 5 * 60, // 5 mins ago
1049 ))?;
1050 assert_eq!(res.metadata.name, "fred");
1051 Ok(())
1052 }
1053
1054 #[test]
1055 fn only_fetches_from_newly_added_relays() -> Result<()> {
1056 let mut m = MockUserManager::default();
1057 let mut client = generate_mock_client();
1058 m.config_manager.expect_load().returning(|| {
1059 Ok(MyConfig {
1060 users: vec![UserRef {
1061 relays: UserRelays {
1062 relays: vec![expected_userrelayrefs_write1()],
1063 created_at: 0,
1064 },
1065 ..generate_standard_config().users[0].clone()
1066 }],
1067 ..generate_standard_config()
1068 })
1069 });
1070 m.config_manager.expect_save().returning(|_| Ok(()));
1071 client
1072 .expect_get_events()
1073 .times(2)
1074 .withf(move |relays, _filters| {
1075 vec![expected_userrelayrefs_write1().url].eq(relays)
1076 || vec![expected_userrelayrefs_read_write1().url].eq(relays)
1077 })
1078 .returning(|relays, _| {
1079 if vec![expected_userrelayrefs_write1().url].eq(&relays) {
1080 Ok(vec![generate_relaylist_event()])
1081 } else if vec![expected_userrelayrefs_read_write1().url].eq(&relays) {
1082 Ok(vec![generate_test_key_1_metadata_event("fred")])
1083 } else {
1084 Ok(vec![])
1085 }
1086 });
1087
1088 let res = futures::executor::block_on(m.get_user(
1089 &client,
1090 &TEST_KEY_1_KEYS.public_key(),
1091 5 * 60, // 5 mins ago
1092 ))?;
1093 assert_eq!(res.metadata.name, "fred");
1094 Ok(())
1095 }
1096 }
961 } 1097 }
962 1098
963 #[test] 1099 #[test]