diff --git a/Cargo.lock b/Cargo.lock index a0ad2d91a..2aec593a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1525,7 +1525,7 @@ dependencies = [ [[package]] name = "mutiny-wasm" -version = "1.14.4" +version = "1.14.5" dependencies = [ "anyhow", "async-trait", diff --git a/mutiny-core/src/lib.rs b/mutiny-core/src/lib.rs index c9fac490b..7d07e83cb 100644 --- a/mutiny-core/src/lib.rs +++ b/mutiny-core/src/lib.rs @@ -28,7 +28,7 @@ mod networking; mod node; pub mod nodemanager; mod onchain; -mod peermanager; +pub mod peermanager; pub mod scorer; pub mod storage; mod subscription; @@ -51,6 +51,7 @@ use crate::messagehandler::CommonLnEventCallback; use crate::nodemanager::NodeManager; use crate::nodemanager::{ChannelClosure, MutinyBip21RawMaterials}; pub use crate::onchain::BroadcastTx1InMultiOut; +use crate::peermanager::CONNECTED_PEER_MANAGER; use crate::storage::{get_invoice_by_hash, DEVICE_LOCK_KEY, LND_CHANNELS_SNAPSHOT_KEY}; use crate::utils::{now, sleep, spawn, spawn_with_handle, StopHandle}; use crate::vss::VSS_MANAGER; @@ -847,9 +848,11 @@ impl<S: MutinyStorage> MutinyWalletBuilder<S> { }; let pending = VSS_MANAGER.get_pending_writes(); - if pending.is_empty() - || (pending.len() == 1 && pending.iter().any(|(key, _)| key == DEVICE_LOCK_KEY)) - { + let only_device_lock_vss_pending = + pending.len() == 1 && pending.iter().any(|(key, _)| key == DEVICE_LOCK_KEY); + let can_update_snapshot = (pending.is_empty() || only_device_lock_vss_pending) + && CONNECTED_PEER_MANAGER.is_any_connected(); + if can_update_snapshot { let second_lnd_snapshot = match fetch_lnd_channels_snapshot(&Client::new(), lsp_url, &node_id, &logger).await { diff --git a/mutiny-core/src/messagehandler.rs b/mutiny-core/src/messagehandler.rs index 18f2e7c64..81d06769e 100644 --- a/mutiny-core/src/messagehandler.rs +++ b/mutiny-core/src/messagehandler.rs @@ -10,6 +10,7 @@ use lightning::util::ser::{Writeable, Writer}; use serde::{Deserialize, Serialize}; use crate::node::LiquidityManager; +use crate::peermanager::CONNECTED_PEER_MANAGER; use crate::storage::MutinyStorage; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] @@ -175,6 +176,14 @@ impl<S: MutinyStorage> CustomMessageHandler for MutinyMessageHandler<S> { msg: &lightning::ln::msgs::Init, inbound: bool, ) -> Result<(), ()> { + CONNECTED_PEER_MANAGER.add_peer( + *their_node_id, + inbound, + msg.remote_network_address + .as_ref() + .map(|addr| format!("{}", addr)), + ); + if let Some(cb) = self.ln_event_callback.clone() { let event = CommonLnEvent::OnConnect { their_node_id: their_node_id.to_string(), @@ -190,6 +199,8 @@ impl<S: MutinyStorage> CustomMessageHandler for MutinyMessageHandler<S> { } fn peer_disconnected(&self, their_node_id: &PublicKey) { + CONNECTED_PEER_MANAGER.remove_peer(their_node_id); + if let Some(cb) = self.ln_event_callback.clone() { let event = CommonLnEvent::OnDisconnect { their_node_id: their_node_id.to_string(), diff --git a/mutiny-core/src/peermanager.rs b/mutiny-core/src/peermanager.rs index 6faaa2abb..ab387f46b 100644 --- a/mutiny-core/src/peermanager.rs +++ b/mutiny-core/src/peermanager.rs @@ -24,8 +24,10 @@ use lightning::routing::gossip::NodeId; use lightning::util::logger::Logger; use lightning::{ln::msgs::SocketAddress, log_warn}; use lightning::{log_debug, log_error}; +use std::collections::HashMap; use std::sync::atomic::AtomicBool; use std::sync::Arc; +use utils::Mutex; #[cfg(target_arch = "wasm32")] use crate::networking::ws_socket::WsTcpSocketDescriptor; @@ -36,6 +38,9 @@ use lightning::ln::peer_handler::SocketDescriptor as LdkSocketDescriptor; #[cfg(target_arch = "wasm32")] use crate::networking::proxy::WsProxy; +pub static CONNECTED_PEER_MANAGER: once_cell::sync::Lazy<ConnectedPeerManager> = + once_cell::sync::Lazy::new(ConnectedPeerManager::default); + #[allow(dead_code)] pub trait PeerManager: Send + Sync + 'static { fn get_peer_node_ids(&self) -> Vec<PublicKey>; @@ -581,3 +586,82 @@ fn try_parse_addr_string(addr: &str) -> (Option<std::net::SocketAddr>, Option<So }); (socket_addr, net_addr) } + +#[derive(Debug, Clone)] +pub struct ConnectedPeerInfo { + pub inbound: bool, + pub remote_address: Option<String>, + pub connected_at_timestamp: u64, +} + +pub struct ConnectedPeerManager { + peers: Arc<Mutex<HashMap<PublicKey, ConnectedPeerInfo>>>, + logger: Mutex<Option<Arc<MutinyLogger>>>, +} + +impl Default for ConnectedPeerManager { + fn default() -> Self { + Self::new() + } +} + +impl ConnectedPeerManager { + pub fn new() -> Self { + Self { + peers: Arc::new(Mutex::new(HashMap::new())), + logger: Mutex::new(None), + } + } + + pub fn set_logger(&self, logger: Arc<MutinyLogger>) { + let mut lock = self.logger.lock().unwrap(); + *lock = Some(logger); + } + + pub fn add_peer( + &self, + their_node_id: PublicKey, + inbound: bool, + remote_address: Option<String>, + ) { + let timestamp = utils::now().as_secs(); + + let info = ConnectedPeerInfo { + inbound, + remote_address, + connected_at_timestamp: timestamp, + }; + + let mut peers = self.peers.lock().unwrap(); + let inserted = peers.insert(their_node_id, info).is_none(); + let logger = { + let guard = self.logger.lock().expect("Failed to lock logger"); + guard.clone() + }; + if inserted { + if let Some(logger) = logger { + log_debug!(logger, "Connected to peer: {}", their_node_id); + } + } + } + + pub fn remove_peer(&self, their_node_id: &PublicKey) { + let mut peers = self.peers.lock().unwrap(); + let removed = peers.remove(their_node_id).is_some(); + + let logger = { + let guard = self.logger.lock().expect("Failed to lock logger"); + guard.clone() + }; + if removed { + if let Some(logger) = logger { + log_debug!(logger, "Disconnected from peer: {}", their_node_id); + } + } + } + + pub fn is_any_connected(&self) -> bool { + let lock = self.peers.lock().unwrap(); + !lock.is_empty() + } +} diff --git a/mutiny-core/src/vss.rs b/mutiny-core/src/vss.rs index 92e61a393..b1f949718 100644 --- a/mutiny-core/src/vss.rs +++ b/mutiny-core/src/vss.rs @@ -250,10 +250,10 @@ impl VssManager { pub fn get_pending_writes(&self) -> Vec<(String, VssPendingWrite)> { self.check_timeout(); - let writes = self.pending_writes.lock().expect( - " - Failed to lock pending writes", - ); + let writes = self + .pending_writes + .lock() + .expect("Failed to lock pending writes"); writes.iter().map(|(k, v)| (k.clone(), v.clone())).collect() } @@ -271,33 +271,30 @@ impl VssManager { } pub fn on_complete_write(&self, key: String) { - let mut pending_writes = self.pending_writes.lock().expect( - " - Failed to lock pending writes", - ); + let mut pending_writes = self + .pending_writes + .lock() + .expect("Failed to lock pending writes"); pending_writes.remove(&key); } pub fn has_in_progress(&self) -> bool { self.check_timeout(); - let writes = self.pending_writes.lock().expect( - " - Failed to lock pending writes", - ); + let writes = self + .pending_writes + .lock() + .expect("Failed to lock pending writes"); !writes.is_empty() } fn check_timeout(&self) { let current_time = utils::now().as_secs(); - let mut writes = self.pending_writes.lock().expect( - " - Failed to lock pending writes", - ); + let mut writes = self + .pending_writes + .lock() + .expect("Failed to lock pending writes"); let logger = { - let guard = self.logger.lock().expect( - " - Failed to lock logger", - ); + let guard = self.logger.lock().expect("Failed to lock logger"); guard.clone() }; writes.retain(|key, write| { diff --git a/mutiny-wasm/Cargo.toml b/mutiny-wasm/Cargo.toml index 395de77b7..70b648f08 100644 --- a/mutiny-wasm/Cargo.toml +++ b/mutiny-wasm/Cargo.toml @@ -2,7 +2,7 @@ cargo-features = ["per-package-target"] [package] name = "mutiny-wasm" -version = "1.14.4" +version = "1.14.5" edition = "2021" authors = ["utxostack"] forced-target = "wasm32-unknown-unknown" diff --git a/mutiny-wasm/src/lib.rs b/mutiny-wasm/src/lib.rs index f231b2043..6f3016dfd 100644 --- a/mutiny-wasm/src/lib.rs +++ b/mutiny-wasm/src/lib.rs @@ -32,6 +32,7 @@ use mutiny_core::authmanager::AuthManager; use mutiny_core::encrypt::decrypt_with_password; use mutiny_core::error::MutinyError; use mutiny_core::messagehandler::{CommonLnEvent, CommonLnEventCallback}; +use mutiny_core::peermanager::CONNECTED_PEER_MANAGER; use mutiny_core::storage::{DeviceLock, MutinyStorage, DEVICE_LOCK_KEY}; use mutiny_core::utils::sleep; use mutiny_core::vss::{MutinyVssClient, VSS_MANAGER}; @@ -225,6 +226,7 @@ impl MutinyWallet { log_info!(logger, "Node version {version}"); VSS_MANAGER.set_logger(logger.clone()); + CONNECTED_PEER_MANAGER.set_logger(logger.clone()); let cipher = password .as_ref()