diff --git a/teos/build.rs b/teos/build.rs index aa9031fd..8d0f131c 100644 --- a/teos/build.rs +++ b/teos/build.rs @@ -2,7 +2,7 @@ fn main() -> Result<(), Box> { tonic_build::configure() .extern_path(".common.teos.v2", "::teos-common::protos") .type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]") - .field_attribute("user_id", "#[serde(with = \"hex::serde\")]") + .field_attribute("GetUserRequest.user_id", "#[serde(with = \"hex::serde\")]") .field_attribute("tower_id", "#[serde(with = \"hex::serde\")]") .field_attribute( "user_ids", diff --git a/teos/proto/teos/v2/appointment.proto b/teos/proto/teos/v2/appointment.proto index 67c66797..07aab6f1 100644 --- a/teos/proto/teos/v2/appointment.proto +++ b/teos/proto/teos/v2/appointment.proto @@ -5,8 +5,10 @@ import "common/teos/v2/appointment.proto"; message GetAppointmentsRequest { // Request the information of appointments with specific locator. + // If a user id is provided (optional), request only appointments belonging to that user. bytes locator = 1; + optional bytes user_id = 2; } message GetAppointmentsResponse { diff --git a/teos/src/api/internal.rs b/teos/src/api/internal.rs index fc2085d8..7a5e5ee5 100644 --- a/teos/src/api/internal.rs +++ b/teos/src/api/internal.rs @@ -2,7 +2,6 @@ use std::sync::{Arc, Condvar, Mutex}; use tonic::{Code, Request, Response, Status}; use triggered::Trigger; -use crate::extended_appointment::UUID; use crate::protos as msgs; use crate::protos::private_tower_services_server::PrivateTowerServices; use crate::protos::public_tower_services_server::PublicTowerServices; @@ -280,32 +279,42 @@ impl PrivateTowerServices for Arc { .map_or("an unknown address".to_owned(), |a| a.to_string()) ); - let mut matching_appointments = vec![]; - let locator = Locator::from_slice(&request.into_inner().locator).map_err(|_| { + let req_data = request.into_inner(); + let locator = Locator::from_slice(&req_data.locator).map_err(|_| { Status::new( Code::InvalidArgument, "The provided locator does not match the expected format (16-byte hexadecimal string)", ) })?; - for (_, appointment) in self + let user_id = req_data + .user_id + .map(|id| UserId::from_slice(&id)) + .transpose() + .map_err(|_| { + Status::new( + Code::InvalidArgument, + "The Provided user_id does not match expected format (33-byte hex string)", + ) + })?; + + let mut matching_appointments: Vec = self .watcher - .get_watcher_appointments_with_locator(locator) - .into_iter() - { - matching_appointments.push(common_msgs::AppointmentData { + .get_watcher_appointments_with_locator(locator, user_id) + .into_values() + .map(|appointment| common_msgs::AppointmentData { appointment_data: Some( common_msgs::appointment_data::AppointmentData::Appointment( appointment.inner.into(), ), ), }) - } + .collect(); - for (_, tracker) in self + for tracker in self .watcher - .get_responder_trackers_with_locator(locator) - .into_iter() + .get_responder_trackers_with_locator(locator, user_id) + .into_values() { matching_appointments.push(common_msgs::AppointmentData { appointment_data: Some(common_msgs::appointment_data::AppointmentData::Tracker( @@ -390,10 +399,9 @@ impl PrivateTowerServices for Arc { Some((info, locators)) => Ok(Response::new(msgs::GetUserResponse { available_slots: info.available_slots, subscription_expiry: info.subscription_expiry, - // TODO: Should make it return locators and make `get_appointments` queryable using the (user_id, locator) pair for consistency. appointments: locators .into_iter() - .map(|locator| UUID::new(locator, user_id).to_vec()) + .map(|locator| locator.to_vec()) .collect(), })), None => Err(Status::new(Code::NotFound, "User not found")), @@ -434,6 +442,8 @@ mod tests_private_api { use bitcoin::hashes::Hash; use bitcoin::Txid; + use rand::{self, thread_rng, Rng}; + use crate::responder::{ConfirmationStatus, TransactionTracker}; use crate::test_utils::{ create_api, generate_dummy_appointment, generate_dummy_appointment_with_user, @@ -442,7 +452,7 @@ mod tests_private_api { use crate::watcher::Breach; use teos_common::cryptography::{self, get_random_keypair}; - use teos_common::test_utils::get_random_user_id; + use teos_common::test_utils::{get_random_locator, get_random_user_id}; #[tokio::test] async fn test_get_all_appointments() { @@ -511,7 +521,23 @@ mod tests_private_api { let locator = Locator::new(get_random_tx().txid()).to_vec(); let response = internal_api - .get_appointments(Request::new(msgs::GetAppointmentsRequest { locator })) + .get_appointments(Request::new(msgs::GetAppointmentsRequest { + locator, + user_id: None, + })) + .await + .unwrap() + .into_inner(); + + assert!(matches!(response, msgs::GetAppointmentsResponse { .. })); + + let user_id = get_random_user_id().to_vec(); + let locator = get_random_locator().to_vec(); + let response = internal_api + .get_appointments(Request::new(msgs::GetAppointmentsRequest { + locator, + user_id: Some(user_id), + })) .await .unwrap() .into_inner(); @@ -520,15 +546,17 @@ mod tests_private_api { } #[tokio::test] - async fn test_get_appointments_watcher() { + async fn test_get_appointments_watcher_without_userid() { let (internal_api, _s) = create_api().await; for i in 0..3 { // Create a dispute tx to be used for creating different dummy appointments with the same locator. let dispute_txid = get_random_tx().txid(); + let locator = Locator::new(dispute_txid); // The number of different appointments to create for this dispute tx. - let appointments_to_create = 4 * i + 7; + let random_number = 4 * i + 7; + let appointments_to_create = random_number; // Add that many appointments to the watcher. for _ in 0..appointments_to_create { @@ -542,12 +570,11 @@ mod tests_private_api { .unwrap(); } - let locator = Locator::new(dispute_txid); - // Query for the current locator and assert it retrieves correct appointments. let response = internal_api .get_appointments(Request::new(msgs::GetAppointmentsRequest { locator: locator.to_vec(), + user_id: None, })) .await .unwrap() @@ -569,6 +596,62 @@ mod tests_private_api { } } + #[tokio::test] + async fn test_get_appointments_watcher_with_userid() { + let (internal_api, _s) = create_api().await; + + for i in 0..3 { + // Create a dispute tx to be used for creating different dummy appointments with the same locator. + let dispute_txid = get_random_tx().txid(); + let locator = Locator::new(dispute_txid); + + // The number of different appointments to create for this dispute tx. + let random_number = 4 * i + 7; + let appointments_to_create = random_number; + + let mut random_users_list = Vec::new(); + + // Add that many appointments to the watcher. + for _ in 0..appointments_to_create { + let (user_sk, user_pk) = get_random_keypair(); + let user_id = UserId(user_pk); + internal_api.watcher.register(user_id).unwrap(); + random_users_list.push(user_id); + let appointment = generate_dummy_appointment(Some(&dispute_txid)).inner; + let signature = cryptography::sign(&appointment.to_vec(), &user_sk).unwrap(); + internal_api + .watcher + .add_appointment(appointment, signature) + .unwrap(); + } + + for user_id in random_users_list.into_iter() { + let response = internal_api + .get_appointments(Request::new(msgs::GetAppointmentsRequest { + locator: locator.to_vec(), + user_id: Some(user_id.to_vec()), + })) + .await + .unwrap() + .into_inner(); + + // Verify that only a single appointment is returned + assert_eq!(response.appointments.len(), 1); + + // Verify that the appointment have the current locator + assert!(matches!( + response.appointments[0].appointment_data, + Some(common_msgs::appointment_data::AppointmentData::Appointment( + common_msgs::Appointment { + locator: ref app_loc, + .. + } + )) if Locator::from_slice(app_loc).unwrap() == locator + )); + } + } + } + #[tokio::test] async fn test_get_appointments_responder() { let (internal_api, _s) = create_api().await; @@ -581,11 +664,19 @@ mod tests_private_api { // The number of different trackers to create for this dispute tx. let trackers_to_create = 4 * i + 7; + let random_tracker_num = thread_rng().gen_range(0..trackers_to_create); + let random_user_id = get_random_user_id(); + // Add that many trackers to the responder. - for _ in 0..trackers_to_create { + for i in 0..trackers_to_create { + let user_id = if i == random_tracker_num { + random_user_id + } else { + get_random_user_id() + }; let tracker = TransactionTracker::new( breach.clone(), - get_random_user_id(), + user_id, ConfirmationStatus::ConfirmedIn(100), ); internal_api @@ -595,16 +686,17 @@ mod tests_private_api { let locator = Locator::new(dispute_tx.txid()); - // Query for the current locator and assert it retrieves correct trackers. + // Query for the current locator without the optional user_id. let response = internal_api .get_appointments(Request::new(msgs::GetAppointmentsRequest { locator: locator.to_vec(), + user_id: None, })) .await .unwrap() .into_inner(); - // The response should contain `trackers_to_create` trackers, all with dispute txid that matches with the locator of the current iteration. + // Verify that the response should contain `trackers_to_create` trackers, all with dispute txid that matches with the locator of the current iteration. assert_eq!(response.appointments.len(), trackers_to_create); for app_data in response.appointments { assert!(matches!( @@ -617,6 +709,28 @@ mod tests_private_api { )) if Locator::new(Txid::from_slice(dispute_txid).unwrap()) == locator )); } + + // Query for the current locator with the optional user_id present. + let response = internal_api + .get_appointments(Request::new(msgs::GetAppointmentsRequest { + locator: locator.to_vec(), + user_id: Some(random_user_id.to_vec()), + })) + .await + .unwrap() + .into_inner(); + + // Verify that only a single appointment is returned and the correct locator is found + assert_eq!(response.appointments.len(), 1); + assert!(matches!( + response.appointments[0].appointment_data, + Some(common_msgs::appointment_data::AppointmentData::Tracker( + common_msgs::Tracker { + ref dispute_txid, + .. + } + )) if Locator::new(Txid::from_slice(dispute_txid).unwrap()) == locator + )); } } @@ -730,11 +844,11 @@ mod tests_private_api { assert!(response.appointments.is_empty()); // Add an appointment and check back - let (uuid, appointment) = generate_dummy_appointment_with_user(user_id, None); + let (_, appointment) = generate_dummy_appointment_with_user(user_id, None); let user_signature = cryptography::sign(&appointment.inner.to_vec(), &user_sk).unwrap(); internal_api .watcher - .add_appointment(appointment.inner, user_signature) + .add_appointment(appointment.inner.clone(), user_signature) .unwrap(); let response = internal_api @@ -747,7 +861,10 @@ mod tests_private_api { assert_eq!(response.available_slots, SLOTS - 1); assert_eq!(response.subscription_expiry, START_HEIGHT as u32 + DURATION); - assert_eq!(response.appointments, Vec::from([uuid.to_vec()])); + assert_eq!( + response.appointments, + Vec::from([appointment.inner.locator.to_vec()]) + ); } #[tokio::test] diff --git a/teos/src/cli.rs b/teos/src/cli.rs index 3ef1d9ff..53c1e2bb 100644 --- a/teos/src/cli.rs +++ b/teos/src/cli.rs @@ -75,20 +75,28 @@ async fn main() { println!("{}", pretty_json(&appointments.into_inner()).unwrap()); } Command::GetAppointments(appointments_data) => { - match Locator::from_hex(&appointments_data.locator) { - Ok(locator) => { - match client - .get_appointments(Request::new(msgs::GetAppointmentsRequest { - locator: locator.to_vec(), - })) - .await - { - Ok(appointments) => { - println!("{}", pretty_json(&appointments.into_inner()).unwrap()) + match appointments_data + .user_id + .map(|id| UserId::from_str(&id).map(|user_id| user_id.to_vec())) + .transpose() + { + Ok(user_id) => match Locator::from_hex(&appointments_data.locator) { + Ok(locator) => { + match client + .get_appointments(Request::new(msgs::GetAppointmentsRequest { + locator: locator.to_vec(), + user_id, + })) + .await + { + Ok(appointments) => { + println!("{}", pretty_json(&appointments.into_inner()).unwrap()) + } + Err(status) => handle_error(status.message()), } - Err(status) => handle_error(status.message()), } - } + Err(e) => handle_error(e), + }, Err(e) => handle_error(e), }; } diff --git a/teos/src/cli_config.rs b/teos/src/cli_config.rs index ba085b8d..8c23c467 100644 --- a/teos/src/cli_config.rs +++ b/teos/src/cli_config.rs @@ -31,6 +31,8 @@ pub struct GetUserData { pub struct GetAppointmentsData { /// The locator of the appointments (16-byte hexadecimal string). pub locator: String, + /// The user identifier (33-byte compressed public key). + pub user_id: Option, } /// Holds all the command line options and commands. diff --git a/teos/src/config.rs b/teos/src/config.rs index 109dd91b..f1db3de0 100644 --- a/teos/src/config.rs +++ b/teos/src/config.rs @@ -18,13 +18,10 @@ pub fn data_dir_absolute_path(data_dir: String) -> PathBuf { pub fn from_file(path: &PathBuf) -> T { match std::fs::read(path) { - Ok(file_content) => toml::from_slice::(&file_content).map_or_else( - |e| { - eprintln!("Couldn't parse config file: {e}"); - T::default() - }, - |config| config, - ), + Ok(file_content) => toml::from_slice::(&file_content).unwrap_or_else(|e| { + eprintln!("Couldn't parse config file: {e}"); + T::default() + }), Err(_) => T::default(), } } @@ -392,7 +389,7 @@ mod tests { assert_eq!(config.api_bind, expected_value); // Check the rest of fields are equal. The easiest is to just the field back and compare with a clone - config.api_bind = config_clone.api_bind.clone(); + config.api_bind.clone_from(&config_clone.api_bind); assert_eq!(config, config_clone); } diff --git a/teos/src/dbm.rs b/teos/src/dbm.rs index a36c9dfb..0c173a54 100644 --- a/teos/src/dbm.rs +++ b/teos/src/dbm.rs @@ -330,23 +330,32 @@ impl DBM { /// matching this locator. If no locator is given, all the appointments in the database would be returned. pub(crate) fn load_appointments( &self, - locator: Option, + locator_and_userid: Option<(Locator, Option)>, ) -> HashMap { let mut appointments = HashMap::new(); let mut sql = "SELECT a.UUID, a.locator, a.encrypted_blob, a.to_self_delay, a.user_signature, a.start_block, a.user_id FROM appointments as a LEFT JOIN trackers as t ON a.UUID=t.UUID WHERE t.UUID IS NULL".to_string(); + // If a locator was passed, filter based on it. - if locator.is_some() { - sql.push_str(" AND a.locator=(?)"); + if locator_and_userid.is_some() { + sql.push_str(" AND a.locator=(?1)"); + } + + // If a user_id is passed, filter even more. + if locator_and_userid.is_some_and(|inner| inner.1.is_some()) { + sql.push_str(" AND a.user_id=(?2)"); } + let mut stmt = self.connection.prepare(&sql).unwrap(); - let mut rows = if let Some(locator) = locator { - stmt.query([locator.to_vec()]).unwrap() - } else { - stmt.query([]).unwrap() + let mut rows = match locator_and_userid { + Some((locator, None)) => stmt.query([locator.to_vec()]).unwrap(), + Some((locator, Some(user_id))) => { + stmt.query([locator.to_vec(), user_id.to_vec()]).unwrap() + } + _ => stmt.query([]).unwrap(), }; while let Ok(Some(row)) = rows.next() { @@ -596,23 +605,32 @@ impl DBM { /// matching this locator. If no locator is given, all the trackers in the database would be returned. pub(crate) fn load_trackers( &self, - locator: Option, + locator_and_userid: Option<(Locator, Option)>, ) -> HashMap { let mut trackers = HashMap::new(); let mut sql = "SELECT t.UUID, t.dispute_tx, t.penalty_tx, t.height, t.confirmed, a.user_id FROM trackers as t INNER JOIN appointments as a ON t.UUID=a.UUID" .to_string(); + // If a locator was passed, filter based on it. - if locator.is_some() { - sql.push_str(" WHERE a.locator=(?)"); + if locator_and_userid.is_some() { + sql.push_str(" AND a.locator=(?1)"); + } + + // If a user_id is passed, filter even more. + if locator_and_userid.is_some_and(|inner| inner.1.is_some()) { + sql.push_str(" AND a.user_id=(?2)"); } + let mut stmt = self.connection.prepare(&sql).unwrap(); - let mut rows = if let Some(locator) = locator { - stmt.query([locator.to_vec()]).unwrap() - } else { - stmt.query([]).unwrap() + let mut rows = match locator_and_userid { + Some((locator, None)) => stmt.query([locator.to_vec()]).unwrap(), + Some((locator, Some(user_id))) => { + stmt.query([locator.to_vec(), user_id.to_vec()]).unwrap() + } + _ => stmt.query([]).unwrap(), }; while let Ok(Some(row)) = rows.next() { @@ -760,8 +778,8 @@ mod tests { use crate::rpc_errors; use crate::test_utils::{ - generate_dummy_appointment, generate_dummy_appointment_with_user, generate_uuid, - get_random_tracker, get_random_tx, AVAILABLE_SLOTS, SUBSCRIPTION_EXPIRY, + generate_dummy_appointment, generate_dummy_appointment_with_user, generate_dummy_tracker, + generate_uuid, get_random_tracker, get_random_tx, AVAILABLE_SLOTS, SUBSCRIPTION_EXPIRY, SUBSCRIPTION_START, }; @@ -1157,7 +1175,7 @@ mod tests { } // Validate that no other appointments than the ones with our locator are returned. - assert_eq!(dbm.load_appointments(Some(locator)), appointments); + assert_eq!(dbm.load_appointments(Some((locator, None))), appointments); // If an appointment has an associated tracker, it should not be loaded since it is seen // as a triggered appointment @@ -1175,7 +1193,61 @@ mod tests { dbm.store_tracker(uuid, &tracker).unwrap(); // We should get all the appointments matching our locator back except from the triggered one - assert_eq!(dbm.load_appointments(Some(locator)), appointments); + assert_eq!(dbm.load_appointments(Some((locator, None))), appointments); + } + + #[test] + fn test_load_appointments_with_locator_and_user_id() { + let dbm = DBM::in_memory().unwrap(); + + let mut appointments = HashMap::new(); + let dispute_tx = get_random_tx(); + let dispute_txid = dispute_tx.txid(); + let locator = Locator::new(dispute_txid); + + // Create user id + let user_id = get_random_user_id(); + let user = UserInfo::new(AVAILABLE_SLOTS, SUBSCRIPTION_START, SUBSCRIPTION_EXPIRY); + dbm.store_user(user_id, &user).unwrap(); + + // Create and store a particular appointment + let (uuid, appointment) = + generate_dummy_appointment_with_user(user_id, Some(&dispute_txid)); + dbm.store_appointment(uuid, &appointment).unwrap(); + appointments.insert(uuid, appointment.clone()); + + // Create random appointments + for _ in 1..11 { + let (uuid, appointment) = generate_dummy_appointment_with_user(user_id, None); + dbm.store_appointment(uuid, &appointment).unwrap(); + appointments.insert(uuid, appointment); + } + + // Verify that no appointment is returned if there is not an exact match of user_id + locator + assert_eq!( + dbm.load_appointments(Some((locator, Some(get_random_user_id()))),), + HashMap::new() + ); + assert_eq!( + dbm.load_appointments(Some((get_random_locator(), Some(user_id))),), + HashMap::new() + ); + + // Verify that the expected appointment is returned, if the correct user_id and locator is given + assert_eq!( + dbm.load_appointments(Some((locator, Some(user_id))),), + HashMap::from([(uuid, appointment)]) + ); + + // Create a tracker from the existing appointment + let tracker = generate_dummy_tracker(user_id, dispute_tx.clone()); + dbm.store_tracker(uuid, &tracker).unwrap(); + + // Verify that an appointment is not returned, if it is triggered (there's a tracker for it) + assert_eq!( + dbm.load_appointments(Some((locator, Some(user_id))),), + HashMap::new() + ); } #[test] @@ -1544,7 +1616,53 @@ mod tests { dbm.store_tracker(uuid, &tracker).unwrap(); } - assert_eq!(dbm.load_trackers(Some(locator)), trackers); + assert_eq!(dbm.load_trackers(Some((locator, None))), trackers); + } + + #[test] + fn test_load_trackers_with_locator_and_user_id() { + let dbm = DBM::in_memory().unwrap(); + let mut trackers = HashMap::new(); + let dispute_tx = get_random_tx(); + let dispute_txid = dispute_tx.txid(); + let locator = Locator::new(dispute_txid); + + // Create user id + let user_id = get_random_user_id(); + let user = UserInfo::new(AVAILABLE_SLOTS, SUBSCRIPTION_START, SUBSCRIPTION_EXPIRY); + dbm.store_user(user_id, &user).unwrap(); + + // Create and store a particular tracker + let (uuid, appointment) = + generate_dummy_appointment_with_user(user_id, Some(&dispute_txid)); + let tracker = generate_dummy_tracker(user_id, dispute_tx.clone()); + dbm.store_appointment(uuid, &appointment).unwrap(); + dbm.store_tracker(uuid, &tracker).unwrap(); + trackers.insert(uuid, tracker.clone()); + + // Create random trackers + for _ in 1..11 { + let (uuid, appointment) = generate_dummy_appointment_with_user(user_id, None); + let tracker = generate_dummy_tracker(user_id, dispute_tx.clone()); + dbm.store_appointment(uuid, &appointment).unwrap(); + dbm.store_tracker(uuid, &tracker).unwrap(); + } + + // Verify that no tracker is returned if there is not an exact match of user_id + locator + assert_eq!( + dbm.load_trackers(Some((locator, Some(get_random_user_id()))),), + HashMap::new() + ); + assert_eq!( + dbm.load_trackers(Some((get_random_locator(), Some(user_id))),), + HashMap::new() + ); + + // Verify that the expected tracker is returned if both the correct user_id and locator are provided + assert_eq!( + dbm.load_trackers(Some((locator, Some(user_id))),), + HashMap::from([(uuid, tracker)]) + ); } #[test] diff --git a/teos/src/test_utils.rs b/teos/src/test_utils.rs index 4dba0a93..2366b195 100644 --- a/teos/src/test_utils.rs +++ b/teos/src/test_utils.rs @@ -341,6 +341,17 @@ pub(crate) fn get_random_tracker( TransactionTracker::new(breach, user_id, status) } +pub(crate) fn generate_dummy_tracker( + user_id: UserId, + dispute_tx: Transaction, +) -> TransactionTracker { + TransactionTracker::new( + Breach::new(dispute_tx.clone(), get_random_tx()), + user_id, + ConfirmationStatus::ConfirmedIn(100), + ) +} + pub(crate) fn store_appointment_and_its_user(dbm: &DBM, appointment: &ExtendedAppointment) { dbm.store_user( appointment.user_id, diff --git a/teos/src/tx_index.rs b/teos/src/tx_index.rs index c9d22b4e..404c0474 100644 --- a/teos/src/tx_index.rs +++ b/teos/src/tx_index.rs @@ -378,7 +378,7 @@ mod tests { // Check that the block data is not in the cache anymore assert_eq!(cache.blocks().len(), cache.size - i - 1); assert!(!cache.blocks().contains(&header.block_hash())); - assert!(cache.tx_in_block.get(&header.block_hash()).is_none()); + assert!(!cache.tx_in_block.contains_key(&header.block_hash())); for locator in locators.iter() { assert!(!cache.contains_key(locator)); } diff --git a/teos/src/watcher.rs b/teos/src/watcher.rs index 037319d7..23147dec 100644 --- a/teos/src/watcher.rs +++ b/teos/src/watcher.rs @@ -423,25 +423,34 @@ impl Watcher { self.dbm.lock().unwrap().load_appointments(None) } - /// Gets all the appointments matching a specific locator from the [Watcher] (from the database). + /// Gets all the appointments matching a specific locator + /// If a user id is provided (optional), only the appointments matching that user are returned pub(crate) fn get_watcher_appointments_with_locator( &self, locator: Locator, + user_id: Option, ) -> HashMap { - self.dbm.lock().unwrap().load_appointments(Some(locator)) + self.dbm + .lock() + .unwrap() + .load_appointments(Some((locator, user_id))) } - /// Gets all the trackers stored in the [Responder] (from the database). + /// Gets all the trackers stored in the [Responder]. pub(crate) fn get_all_responder_trackers(&self) -> HashMap { self.dbm.lock().unwrap().load_trackers(None) } - /// Gets all the trackers matching s specific locator from the [Responder] (from the database). + /// Gets all the trackers matching a specific locator and an optional user id from the [Responder]. pub(crate) fn get_responder_trackers_with_locator( &self, locator: Locator, + user_id: Option, ) -> HashMap { - self.dbm.lock().unwrap().load_trackers(Some(locator)) + self.dbm + .lock() + .unwrap() + .load_trackers(Some((locator, user_id))) } /// Gets the list of all registered user ids. diff --git a/watchtower-plugin/src/main.rs b/watchtower-plugin/src/main.rs index 5778e8d0..1b845a64 100755 --- a/watchtower-plugin/src/main.rs +++ b/watchtower-plugin/src/main.rs @@ -385,7 +385,7 @@ async fn abandon_tower( ) -> Result { let tower_id = TowerId::try_from(v).map_err(|e| anyhow!(e))?; let mut state = plugin.state().lock().unwrap(); - if state.towers.get(&tower_id).is_some() { + if state.towers.contains_key(&tower_id) { state.remove_tower(tower_id).unwrap(); Ok(json!(format!("{tower_id} successfully abandoned"))) } else { diff --git a/watchtower-plugin/src/retrier.rs b/watchtower-plugin/src/retrier.rs index 0920e366..e61c9b33 100644 --- a/watchtower-plugin/src/retrier.rs +++ b/watchtower-plugin/src/retrier.rs @@ -432,7 +432,7 @@ impl Retrier { // Create a new scope so we can get all the data only locking the WTClient once. let (tower_id, status, net_addr, user_id, user_sk, proxy) = { let wt_client = self.wt_client.lock().unwrap(); - if wt_client.towers.get(&self.tower_id).is_none() { + if !wt_client.towers.contains_key(&self.tower_id) { return Err(Error::permanent(RetryError::Abandoned)); }