Skip to content

Commit

Permalink
Getting rid of Watcher::appointments
Browse files Browse the repository at this point in the history
Apart from this having caused a ripple effect in the code base and
tests, there where 2 main issue that needed to be resolved for such a
change to happen:
1- a UUID -> Locator map was needed in `Watcher::get_subscription_info`
(a method used by the public api) since the `UserInfo` held by the
gatekeeper kept track of uuids of a user and not the locators for that
user. This could be resolved by making `UserInfo` hold the users'
locators instead of uuids, and we can get the uuids for these locators
at any time for a specific user.

2- a UUID -> UserId map was needed in `Watcher::filtered_block_connected`
for breached appointments. We could have just loaded the user_id while loading
the appointment from the DB, so this wasn't actually needed.
  • Loading branch information
mariocynicys committed Mar 7, 2023
1 parent 92ad4e5 commit 72b384d
Show file tree
Hide file tree
Showing 7 changed files with 383 additions and 346 deletions.
6 changes: 6 additions & 0 deletions teos-common/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ pub fn get_random_user_id() -> UserId {
UserId(pk)
}

pub fn get_random_locator() -> Locator {
let mut rng = rand::thread_rng();

Locator::from_slice(&rng.gen::<[u8; 16]>()).unwrap()
}

pub fn generate_random_appointment(dispute_txid: Option<&Txid>) -> Appointment {
let dispute_txid = match dispute_txid {
Some(l) => *l,
Expand Down
19 changes: 12 additions & 7 deletions teos/src/api/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ 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;
Expand Down Expand Up @@ -389,7 +390,12 @@ impl PrivateTowerServices for Arc<InternalAPI> {
Some(info) => Ok(Response::new(msgs::GetUserResponse {
available_slots: info.available_slots,
subscription_expiry: info.subscription_expiry,
appointments: info.appointments.keys().map(|uuid| uuid.to_vec()).collect(),
// Should this be a list of uuids or locators?
appointments: info
.appointments
.keys()
.map(|locator| UUID::new(*locator, user_id).to_vec())
.collect(),
})),
None => Err(Status::new(Code::NotFound, "User not found")),
}
Expand Down Expand Up @@ -432,8 +438,8 @@ mod tests_private_api {
use crate::extended_appointment::UUID;
use crate::responder::{ConfirmationStatus, TransactionTracker};
use crate::test_utils::{
create_api, generate_dummy_appointment, generate_uuid, get_random_tx, DURATION, SLOTS,
START_HEIGHT,
create_api, generate_dummy_appointment, generate_dummy_appointment_with_user,
generate_uuid, get_random_tx, DURATION, SLOTS, START_HEIGHT,
};
use crate::watcher::Breach;

Expand Down Expand Up @@ -730,12 +736,11 @@ mod tests_private_api {
assert!(response.appointments.is_empty());

// Add an appointment and check back
let appointment = generate_dummy_appointment(None).inner;
let uuid = UUID::new(appointment.locator, user_id);
let user_signature = cryptography::sign(&appointment.to_vec(), &user_sk).unwrap();
let (uuid, 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.clone(), user_signature)
.add_appointment(appointment.inner, user_signature)
.unwrap();

let response = internal_api
Expand Down
76 changes: 32 additions & 44 deletions teos/src/dbm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,22 +139,22 @@ impl DBM {
}
}

/// Loads the associated appointments ([Appointment]) of a given user ([UserInfo]).
pub(crate) fn load_user_appointments(&self, user_id: UserId) -> HashMap<UUID, u32> {
/// Loads the associated appointments ([Appointment]) of a given user ([UserId]).
pub(crate) fn load_user_appointments(&self, user_id: UserId) -> HashMap<Locator, u32> {
let mut stmt = self
.connection
.prepare("SELECT UUID, encrypted_blob FROM appointments WHERE user_id=(?)")
.prepare("SELECT locator, encrypted_blob FROM appointments WHERE user_id=(?)")
.unwrap();
let mut rows = stmt.query([user_id.to_vec()]).unwrap();

let mut appointments = HashMap::new();
while let Ok(Some(inner_row)) = rows.next() {
let raw_uuid: Vec<u8> = inner_row.get(0).unwrap();
let uuid = UUID::from_slice(&raw_uuid[0..20]).unwrap();
let raw_locator: Vec<u8> = inner_row.get(0).unwrap();
let locator = Locator::from_slice(&raw_locator).unwrap();
let e_blob: Vec<u8> = inner_row.get(1).unwrap();

appointments.insert(
uuid,
locator,
compute_appointment_slots(e_blob.len(), ENCRYPTED_BLOB_MAX_SIZE),
);
}
Expand Down Expand Up @@ -219,6 +219,15 @@ impl DBM {
(users.len() as f64 / limit as f64).ceil() as usize
}

/// Get the number of stored appointments.
pub(crate) fn get_appointments_count(&self) -> usize {
let mut stmt = self
.connection
.prepare("SELECT COUNT(*) FROM appointments as a LEFT JOIN trackers as t ON a.UUID=t.UUID WHERE t.UUID IS NULL")
.unwrap();
stmt.query_row([], |row| row.get(0)).unwrap()
}

/// Stores an [Appointment] into the database.
pub(crate) fn store_appointment(
&self,
Expand Down Expand Up @@ -433,7 +442,6 @@ impl DBM {
(appointments.len() as f64 / limit as f64).ceil() as usize
}

/// Loads the locator associated to a given UUID
pub(crate) fn load_locator(&self, uuid: UUID) -> Result<Locator, Error> {
let mut stmt = self
.connection
Expand Down Expand Up @@ -516,7 +524,7 @@ impl DBM {
let mut stmt = self
.connection
.prepare(
"SELECT t.UUID, t.penalty_tx, t.height, t.confirmed, a.user_id
"SELECT t.UUID, t.penalty_tx, t.dispute_tx, t.height, t.confirmed, a.user_id
FROM trackers as t INNER JOIN appointments as a ON t.UUID=a.UUID",
)
.unwrap();
Expand All @@ -525,16 +533,23 @@ impl DBM {
while let Ok(Some(row)) = rows.next() {
let raw_uuid: Vec<u8> = row.get(0).unwrap();
let raw_penalty_tx: Vec<u8> = row.get(1).unwrap();
let height: u32 = row.get(2).unwrap();
let confirmed: bool = row.get(3).unwrap();
let raw_userid: Vec<u8> = row.get(4).unwrap();
let raw_dispute_tx: Vec<u8> = row.get(2).unwrap();
let height: u32 = row.get(3).unwrap();
let confirmed: bool = row.get(4).unwrap();
let raw_user_id: Vec<u8> = row.get(5).unwrap();

let dispute_txid = consensus::deserialize::<bitcoin::Transaction>(&raw_dispute_tx)
.unwrap()
.txid();
let penalty_txid = consensus::deserialize::<bitcoin::Transaction>(&raw_penalty_tx)
.unwrap()
.txid();
summaries.insert(
UUID::from_slice(&raw_uuid).unwrap(),
TrackerSummary::new(
UserId::from_slice(&raw_userid).unwrap(),
consensus::deserialize::<bitcoin::Transaction>(&raw_penalty_tx)
.unwrap()
.txid(),
UserId::from_slice(&raw_user_id).unwrap(),
dispute_txid,
penalty_txid,
ConfirmationStatus::from_db_data(height, confirmed),
),
);
Expand Down Expand Up @@ -728,7 +743,7 @@ mod tests {
for _ in 0..10 {
let (uuid, appointment) = generate_dummy_appointment_with_user(user_id, None);
dbm.store_appointment(uuid, &appointment).unwrap();
user.appointments.insert(uuid, 1);
user.appointments.insert(appointment.locator(), 1);
}

// Check both loading the whole user info or only the associated appointments
Expand Down Expand Up @@ -782,7 +797,7 @@ mod tests {
.get_mut(&user_id)
.unwrap()
.appointments
.insert(uuid, 1);
.insert(appointment.locator(), 1);
}
}

Expand Down Expand Up @@ -1139,33 +1154,6 @@ mod tests {
// Test it does not fail even if the user does not exist (it will log though)
dbm.batch_remove_appointments(&appointments, &HashMap::new());
}
#[test]
fn test_load_locator() {
let dbm = DBM::in_memory().unwrap();

// In order to add an appointment we need the associated user to be present
let user_id = get_random_user_id();
let user = UserInfo::new(AVAILABLE_SLOTS, SUBSCRIPTION_START, SUBSCRIPTION_EXPIRY);
dbm.store_user(user_id, &user).unwrap();

let (uuid, appointment) = generate_dummy_appointment_with_user(user_id, None);

assert!(matches!(
dbm.store_appointment(uuid, &appointment),
Ok { .. }
));

// We should be able to load the locator now the appointment exists
assert_eq!(dbm.load_locator(uuid).unwrap(), appointment.locator());
}

#[test]
fn test_load_nonexistent_locator() {
let dbm = DBM::in_memory().unwrap();

let (uuid, _) = generate_dummy_appointment_with_user(get_random_user_id(), None);
assert!(matches!(dbm.load_locator(uuid), Err(Error::NotFound)));
}

#[test]
fn test_store_load_tracker() {
Expand Down
Loading

0 comments on commit 72b384d

Please sign in to comment.