diff --git a/teos-common/build.rs b/teos-common/build.rs index 52c390f..a66e76f 100644 --- a/teos-common/build.rs +++ b/teos-common/build.rs @@ -6,6 +6,10 @@ fn main() -> Result<(), Box> { .field_attribute("appointment_data", "#[serde(rename = \"appointment\")]") .field_attribute("user_id", "#[serde(with = \"hex::serde\")]") .field_attribute("locator", "#[serde(with = \"hex::serde\")]") + .field_attribute( + "locators", + "#[serde(with = \"crate::ser::serde_vec_bytes\")]", + ) .field_attribute("encrypted_blob", "#[serde(with = \"hex::serde\")]") .field_attribute("dispute_txid", "#[serde(with = \"crate::ser::serde_be\")]") .field_attribute("penalty_txid", "#[serde(with = \"crate::ser::serde_be\")]") diff --git a/teos-common/src/ser.rs b/teos-common/src/ser.rs index 663ff66..4946ba3 100644 --- a/teos-common/src/ser.rs +++ b/teos-common/src/ser.rs @@ -56,6 +56,54 @@ pub mod serde_be { } } +pub mod serde_vec_bytes { + use super::*; + use serde::de::{self, Deserializer, SeqAccess}; + + pub fn serialize(v: &[Vec], s: S) -> Result + where + S: Serializer, + { + let mut seq = s.serialize_seq(Some(v.len()))?; + for element in v.iter() { + seq.serialize_element(&hex::encode(element))?; + } + seq.end() + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + struct VecVisitor; + + impl<'de> de::Visitor<'de> for VecVisitor { + type Value = Vec>; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a hex encoded string") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut result = Vec::new(); + while let Some(v) = seq.next_element::()? { + result + .push(hex::decode(v).map_err(|_| { + de::Error::custom("cannot deserialize the given value") + })?); + } + + Ok(result) + } + } + + deserializer.deserialize_any(VecVisitor) + } +} + pub mod serde_status { use super::*; use serde::de::{self, Deserializer}; diff --git a/teos/build.rs b/teos/build.rs index 56482d4..0bd1477 100644 --- a/teos/build.rs +++ b/teos/build.rs @@ -6,11 +6,11 @@ fn main() -> Result<(), Box> { .field_attribute("tower_id", "#[serde(with = \"hex::serde\")]") .field_attribute( "user_ids", - "#[serde(serialize_with = \"crate::api::http::serialize_vec_bytes\")]", + "#[serde(serialize_with = \"teos_common::ser::serde_vec_bytes::serialize\")]", ) .field_attribute( "GetUserResponse.appointments", - "#[serde(serialize_with = \"crate::api::http::serialize_vec_bytes\")]", + "#[serde(serialize_with = \"teos_common::ser::serde_vec_bytes::serialize\")]", ) .compile( &[ diff --git a/teos/src/api/http.rs b/teos/src/api/http.rs index 177f8c6..644de14 100644 --- a/teos/src/api/http.rs +++ b/teos/src/api/http.rs @@ -1,4 +1,4 @@ -use serde::{ser::SerializeSeq, Deserialize, Serialize, Serializer}; +use serde::{Deserialize, Serialize}; use std::convert::Infallible; use std::error::Error; use std::net::SocketAddr; @@ -58,17 +58,6 @@ impl ApiError { } } -pub fn serialize_vec_bytes(v: &[Vec], s: S) -> Result -where - S: Serializer, -{ - let mut seq = s.serialize_seq(Some(v.len()))?; - for element in v.iter() { - seq.serialize_element(&hex::encode(element))?; - } - seq.end() -} - fn with_grpc( grpc_endpoint: PublicTowerServicesClient, ) -> impl Filter,), Error = Infallible> + Clone { diff --git a/watchtower-plugin/README.md b/watchtower-plugin/README.md index 815a1a7..9afe8ab 100644 --- a/watchtower-plugin/README.md +++ b/watchtower-plugin/README.md @@ -9,8 +9,9 @@ commitment transaction is generated. It also keeps a summary of the messages sen The plugin has the following methods: - `registertower tower_id` : registers the user id (compressed public key) with a given tower. -- `list_towers`: lists all registered towers. +- `listtowers`: lists all registered towers. - `gettowerinfo tower_id`: gets all the locally stored data about a given tower. +- `getsubscriptioninfo tower_id`: gets the subscription information by querying the tower. - `retrytower tower_id`: tries to send pending appointment to a (previously) unreachable tower. - `getappointment tower_id locator`: queries a given tower about an appointment. diff --git a/watchtower-plugin/src/main.rs b/watchtower-plugin/src/main.rs index b4d14e5..c5f3a16 100755 --- a/watchtower-plugin/src/main.rs +++ b/watchtower-plugin/src/main.rs @@ -180,6 +180,48 @@ async fn get_appointment( Ok(json!(response)) } +/// Gets the subscription information directly form the tower. +async fn get_subscription_info( + plugin: Plugin>>, + v: serde_json::Value, +) -> Result { + let tower_id = TowerId::try_from(v).map_err(|x| anyhow!(x))?; + + let user_sk = plugin.state().lock().unwrap().user_sk; + let tower_net_addr = { + let state = plugin.state().lock().unwrap(); + if let Some(info) = state.towers.get(&tower_id) { + Ok(info.net_addr.clone()) + } else { + Err(anyhow!("Unknown tower id: {}", tower_id)) + } + }?; + + let get_subscription_info = format!("{}/get_subscription_info", tower_net_addr); + let signature = cryptography::sign("get subscription info".as_bytes(), &user_sk).unwrap(); + + let response: common_msgs::GetSubscriptionInfoResponse = process_post_response( + post_request( + &get_subscription_info, + &common_msgs::GetSubscriptionInfoRequest { signature }, + ) + .await, + ) + .await + .map_err(|e| { + if e.is_connection() { + plugin + .state() + .lock() + .unwrap() + .set_tower_status(tower_id, TowerStatus::TemporaryUnreachable); + } + to_cln_error(e) + })?; + + Ok(json!(response)) +} + /// Lists all the registered towers. /// /// The given information comes from memory, so it is summarized. @@ -408,6 +450,11 @@ async fn main() -> Result<(), Error> { "Gets appointment data from the tower given the tower id and the locator.", get_appointment, ) + .rpcmethod( + "getsubscriptioninfo", + "Gets the subscription information directly from the tower.", + get_subscription_info, + ) .rpcmethod("listtowers", "Lists all registered towers.", list_towers) .rpcmethod( "gettowerinfo",