|
1 | 1 | use std::collections::{HashMap, HashSet};
|
2 | 2 | use std::fmt;
|
| 3 | +use std::sync::MutexGuard; |
| 4 | +use std::sync::{Arc, Mutex}; |
3 | 5 |
|
4 | 6 | use serde::{Deserialize, Serialize};
|
5 | 7 |
|
6 | 8 | use teos_common::appointment::{Appointment, Locator};
|
7 | 9 | use teos_common::net::NetAddr;
|
8 | 10 | use teos_common::receipts::AppointmentReceipt;
|
9 | 11 | use teos_common::TowerId;
|
| 12 | +use teos_common::{cryptography, errors}; |
| 13 | + |
| 14 | +use crate::convert::CommitmentRevocation; |
| 15 | +use crate::http::AddAppointmentError; |
| 16 | +use crate::net::http; |
| 17 | +use crate::wt_client::{RevocationData, WTClient}; |
10 | 18 |
|
11 | 19 | pub mod convert;
|
12 | 20 | pub mod net;
|
@@ -280,6 +288,143 @@ impl MisbehaviorProof {
|
280 | 288 | }
|
281 | 289 | }
|
282 | 290 |
|
| 291 | +/// Sends an appointment to all registered towers for every new commitment transaction. |
| 292 | +/// |
| 293 | +/// The appointment is built using the data provided by the backend (dispute txid and penalty transaction). |
| 294 | +pub async fn on_commitment_revocation( |
| 295 | + wt_client: Arc<Mutex<WTClient>>, |
| 296 | + commitment_revocation: CommitmentRevocation, |
| 297 | +) -> Result<(), Box<dyn std::error::Error>> { |
| 298 | + log::debug!( |
| 299 | + "New commitment revocation received for channel {}. Commit number {}", |
| 300 | + commitment_revocation.channel_id, |
| 301 | + commitment_revocation.commit_num |
| 302 | + ); |
| 303 | + |
| 304 | + // TODO: For now, to_self_delay is hardcoded to 42. Revisit and define it better / remove it when / if needed |
| 305 | + let locator = Locator::new(commitment_revocation.commitment_txid); |
| 306 | + let appointment = Appointment::new( |
| 307 | + locator, |
| 308 | + cryptography::encrypt( |
| 309 | + &commitment_revocation.penalty_tx, |
| 310 | + &commitment_revocation.commitment_txid, |
| 311 | + ) |
| 312 | + .unwrap(), |
| 313 | + 42, |
| 314 | + ); |
| 315 | + let signature = |
| 316 | + cryptography::sign(&appointment.to_vec(), &wt_client.lock().unwrap().user_sk).unwrap(); |
| 317 | + |
| 318 | + // Looks like we cannot iterate through towers given a locked state is not Send (due to the async call), |
| 319 | + // so we need to clone the bare minimum. |
| 320 | + let towers = wt_client |
| 321 | + .lock() |
| 322 | + .unwrap() |
| 323 | + .towers |
| 324 | + .iter() |
| 325 | + .map(|(id, info)| (*id, info.net_addr.clone(), info.status)) |
| 326 | + .collect::<Vec<_>>(); |
| 327 | + |
| 328 | + for (tower_id, net_addr, status) in towers { |
| 329 | + if status.is_reachable() { |
| 330 | + match http::add_appointment(tower_id, &net_addr, &appointment, &signature).await { |
| 331 | + Ok((slots, receipt)) => { |
| 332 | + wt_client |
| 333 | + .lock() |
| 334 | + .unwrap() |
| 335 | + .add_appointment_receipt(tower_id, locator, slots, &receipt); |
| 336 | + log::debug!("Response verified and data stored in the database"); |
| 337 | + } |
| 338 | + Err(e) => match e { |
| 339 | + AddAppointmentError::RequestError(e) => { |
| 340 | + if e.is_connection() { |
| 341 | + log::warn!( |
| 342 | + "{tower_id} cannot be reached. Adding {} to pending appointments", |
| 343 | + appointment.locator |
| 344 | + ); |
| 345 | + let mut state = wt_client.lock().unwrap(); |
| 346 | + state.set_tower_status(tower_id, TowerStatus::TemporaryUnreachable); |
| 347 | + state.add_pending_appointment(tower_id, &appointment); |
| 348 | + send_to_retrier(&state, tower_id, appointment.locator); |
| 349 | + } |
| 350 | + } |
| 351 | + AddAppointmentError::ApiError(e) => match e.error_code { |
| 352 | + errors::INVALID_SIGNATURE_OR_SUBSCRIPTION_ERROR => { |
| 353 | + log::warn!( |
| 354 | + "There is a subscription issue with {tower_id}. Adding {} to pending", |
| 355 | + appointment.locator |
| 356 | + ); |
| 357 | + let mut state = wt_client.lock().unwrap(); |
| 358 | + state.set_tower_status(tower_id, TowerStatus::SubscriptionError); |
| 359 | + state.add_pending_appointment(tower_id, &appointment); |
| 360 | + send_to_retrier(&state, tower_id, appointment.locator); |
| 361 | + } |
| 362 | + |
| 363 | + _ => { |
| 364 | + log::warn!( |
| 365 | + "{tower_id} rejected the appointment. Error: {}, error_code: {}", |
| 366 | + e.error, |
| 367 | + e.error_code |
| 368 | + ); |
| 369 | + wt_client |
| 370 | + .lock() |
| 371 | + .unwrap() |
| 372 | + .add_invalid_appointment(tower_id, &appointment); |
| 373 | + } |
| 374 | + }, |
| 375 | + AddAppointmentError::SignatureError(proof) => { |
| 376 | + log::warn!("Cannot recover known tower_id from the appointment receipt. Flagging tower as misbehaving"); |
| 377 | + wt_client |
| 378 | + .lock() |
| 379 | + .unwrap() |
| 380 | + .flag_misbehaving_tower(tower_id, proof) |
| 381 | + } |
| 382 | + }, |
| 383 | + }; |
| 384 | + } else if status.is_misbehaving() { |
| 385 | + log::warn!("{tower_id} is misbehaving. Not sending any further appointments",); |
| 386 | + } else { |
| 387 | + if status.is_subscription_error() { |
| 388 | + log::warn!( |
| 389 | + "There is a subscription issue with {tower_id}. Adding {} to pending", |
| 390 | + appointment.locator |
| 391 | + ); |
| 392 | + } else { |
| 393 | + log::warn!( |
| 394 | + "{tower_id} is {status}. Adding {} to pending", |
| 395 | + appointment.locator, |
| 396 | + ); |
| 397 | + } |
| 398 | + |
| 399 | + let mut state = wt_client.lock().unwrap(); |
| 400 | + state.add_pending_appointment(tower_id, &appointment); |
| 401 | + |
| 402 | + if !status.is_unreachable() { |
| 403 | + send_to_retrier(&state, tower_id, appointment.locator); |
| 404 | + } |
| 405 | + } |
| 406 | + } |
| 407 | + |
| 408 | + Ok(()) |
| 409 | +} |
| 410 | + |
| 411 | +/// Sends fresh data to a retrier as long as is does not exist, or it does and its running. |
| 412 | +fn send_to_retrier(state: &MutexGuard<WTClient>, tower_id: TowerId, locator: Locator) { |
| 413 | + if if let Some(status) = state.get_retrier_status(&tower_id) { |
| 414 | + // A retrier in the retriers map can only be running or idle |
| 415 | + status.is_running() |
| 416 | + } else { |
| 417 | + true |
| 418 | + } { |
| 419 | + state |
| 420 | + .unreachable_towers |
| 421 | + .send((tower_id, RevocationData::Fresh(locator))) |
| 422 | + .unwrap(); |
| 423 | + } else { |
| 424 | + log::debug!("Not sending data to idle retrier ({tower_id}, {locator})") |
| 425 | + } |
| 426 | +} |
| 427 | + |
283 | 428 | #[cfg(test)]
|
284 | 429 | mod tests {
|
285 | 430 | use super::*;
|
|
0 commit comments