diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2023-11-01 00:00:00 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2023-11-01 00:00:00 +0000 |
| commit | 497bf71910f0f224ce66b154d58a228095a40c0a (patch) | |
| tree | a3d55f0581a27465ea9ab2fd03956881ee13193a /src/key_handling | |
| parent | e237328ec611a5891586530c1d3cb26c16c1093b (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/key_handling')
| -rw-r--r-- | src/key_handling/users.rs | 320 |
1 files changed, 228 insertions, 92 deletions
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] |