diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 8b8f09fdca3..2da1e72234e 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -204,6 +204,7 @@ impl TestChainMonitor { logger.clone(), feeest, Arc::clone(&persister), + keys.get_peer_storage_key(), )), logger, keys, @@ -337,6 +338,10 @@ impl NodeSigner for KeyProvider { unreachable!() } + fn get_peer_storage_key(&self) -> [u8; 32] { + SecretKey::from_slice(&[42; 32]).unwrap().secret_bytes() + } + fn sign_bolt12_invoice( &self, _invoice: &UnsignedBolt12Invoice, ) -> Result { diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index 6594f472516..04c5415b592 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -242,6 +242,7 @@ type PeerMan<'a> = PeerManager< Arc, IgnoringMessageHandler, Arc, + IgnoringMessageHandler, >; struct MoneyLossDetector<'a> { @@ -420,6 +421,10 @@ impl NodeSigner for KeyProvider { let secp_ctx = Secp256k1::signing_only(); Ok(secp_ctx.sign_ecdsa(&msg_hash, &self.node_secret)) } + + fn get_peer_storage_key(&self) -> [u8; 32] { + SecretKey::from_slice(&[42; 32]).unwrap().secret_bytes() + } } impl SignerProvider for KeyProvider { @@ -608,20 +613,23 @@ pub fn do_test(mut data: &[u8], logger: &Arc) { ]; let broadcast = Arc::new(TestBroadcaster { txn_broadcasted: Mutex::new(Vec::new()) }); + + let keys_manager = Arc::new(KeyProvider { + node_secret: our_network_key.clone(), + inbound_payment_key: ExpandedKey::new(inbound_payment_key), + counter: AtomicU64::new(0), + signer_state: RefCell::new(new_hash_map()), + }); + let monitor = Arc::new(chainmonitor::ChainMonitor::new( None, broadcast.clone(), Arc::clone(&logger), fee_est.clone(), Arc::new(TestPersister { update_ret: Mutex::new(ChannelMonitorUpdateStatus::Completed) }), + keys_manager.get_peer_storage_key(), )); - let keys_manager = Arc::new(KeyProvider { - node_secret: our_network_key.clone(), - inbound_payment_key: ExpandedKey::new(inbound_payment_key), - counter: AtomicU64::new(0), - signer_state: RefCell::new(new_hash_map()), - }); let network = Network::Bitcoin; let best_block_timestamp = genesis_block(network).header.time; let params = ChainParameters { network, best_block: BestBlock::from_network(network) }; @@ -653,6 +661,7 @@ pub fn do_test(mut data: &[u8], logger: &Arc) { route_handler: gossip_sync.clone(), onion_message_handler: IgnoringMessageHandler {}, custom_message_handler: IgnoringMessageHandler {}, + send_only_message_handler: IgnoringMessageHandler {}, }; let random_data = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index 71836995017..50e0cef3f94 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -246,6 +246,10 @@ impl NodeSigner for KeyProvider { ) -> Result { unreachable!() } + + fn get_peer_storage_key(&self) -> [u8; 32] { + unreachable!() + } } impl SignerProvider for KeyProvider { diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs index 46d990bb37e..845f79704e1 100644 --- a/lightning-background-processor/src/lib.rs +++ b/lightning-background-processor/src/lib.rs @@ -649,7 +649,7 @@ use futures_util::{dummy_waker, OptionalSelector, Selector, SelectorOutput}; /// # type ChannelManager = lightning::ln::channelmanager::SimpleArcChannelManager, B, FE, Logger>; /// # type OnionMessenger = lightning::onion_message::messenger::OnionMessenger, Arc, Arc, Arc>, Arc, Arc, Arc>>, Arc>, lightning::ln::peer_handler::IgnoringMessageHandler, lightning::ln::peer_handler::IgnoringMessageHandler, lightning::ln::peer_handler::IgnoringMessageHandler>; /// # type Scorer = RwLock, Arc>>; -/// # type PeerManager = lightning::ln::peer_handler::SimpleArcPeerManager, B, FE, Arc
    , Logger>; +/// # type PeerManager = lightning::ln::peer_handler::SimpleArcPeerManager, B, FE, Arc
      , Logger, F, Store>; /// # /// # struct Node< /// # B: lightning::chain::chaininterface::BroadcasterInterface + Send + Sync + 'static, @@ -1085,7 +1085,7 @@ mod tests { use lightning::routing::gossip::{NetworkGraph, P2PGossipSync}; use lightning::routing::router::{CandidateRouteHop, DefaultRouter, Path, RouteHop}; use lightning::routing::scoring::{ChannelUsage, LockableScore, ScoreLookUp, ScoreUpdate}; - use lightning::sign::{ChangeDestinationSource, InMemorySigner, KeysManager}; + use lightning::sign::{ChangeDestinationSource, InMemorySigner, KeysManager, NodeSigner}; use lightning::types::features::{ChannelFeatures, NodeFeatures}; use lightning::types::payment::PaymentHash; use lightning::util::config::UserConfig; @@ -1208,6 +1208,7 @@ mod tests { Arc, IgnoringMessageHandler, Arc, + IgnoringMessageHandler, >, >, chain_monitor: Arc, @@ -1568,6 +1569,7 @@ mod tests { logger.clone(), fee_estimator.clone(), kv_store.clone(), + keys_manager.get_peer_storage_key(), )); let best_block = BestBlock::from_network(network); let params = ChainParameters { network, best_block }; @@ -1621,6 +1623,7 @@ mod tests { route_handler: Arc::new(test_utils::TestRoutingMessageHandler::new()), onion_message_handler: messenger.clone(), custom_message_handler: IgnoringMessageHandler {}, + send_only_message_handler: IgnoringMessageHandler {}, }; let peer_manager = Arc::new(PeerManager::new( msg_handler, diff --git a/lightning-liquidity/src/manager.rs b/lightning-liquidity/src/manager.rs index afaac25cdd2..a945fa80da1 100644 --- a/lightning-liquidity/src/manager.rs +++ b/lightning-liquidity/src/manager.rs @@ -300,7 +300,7 @@ where { /// # type MyFilter = dyn lightning::chain::Filter + Send + Sync; /// # type MyLogger = dyn lightning::util::logger::Logger + Send + Sync; /// # type MyChainMonitor = lightning::chain::chainmonitor::ChainMonitor, Arc, Arc, Arc, Arc>; - /// # type MyPeerManager = lightning::ln::peer_handler::SimpleArcPeerManager, MyLogger>; + /// # type MyPeerManager = lightning::ln::peer_handler::SimpleArcPeerManager, MyLogger, MyFilter, MyStore>; /// # type MyNetworkGraph = lightning::routing::gossip::NetworkGraph>; /// # type MyGossipSync = lightning::routing::gossip::P2PGossipSync, Arc, Arc>; /// # type MyChannelManager = lightning::ln::channelmanager::SimpleArcChannelManager; diff --git a/lightning-liquidity/tests/common/mod.rs b/lightning-liquidity/tests/common/mod.rs index f114f7b9c89..17a0d10dc9f 100644 --- a/lightning-liquidity/tests/common/mod.rs +++ b/lightning-liquidity/tests/common/mod.rs @@ -5,7 +5,7 @@ #![allow(unused_macros)] use lightning::chain::Filter; -use lightning::sign::EntropySource; +use lightning::sign::{EntropySource, NodeSigner}; use bitcoin::blockdata::constants::{genesis_block, ChainHash}; use bitcoin::blockdata::transaction::Transaction; @@ -130,6 +130,7 @@ pub(crate) struct Node { >, >, Arc, + Arc, >, >, pub(crate) liquidity_manager: @@ -430,6 +431,7 @@ pub(crate) fn create_liquidity_node( logger.clone(), fee_estimator.clone(), kv_store.clone(), + keys_manager.get_peer_storage_key(), )); let best_block = BestBlock::from_network(network); let chain_params = ChainParameters { network, best_block }; @@ -465,6 +467,7 @@ pub(crate) fn create_liquidity_node( chan_handler: Arc::new(test_utils::TestChannelMessageHandler::new( ChainHash::using_genesis_block(Network::Testnet), )), + send_only_message_handler: Arc::clone(&chain_monitor), route_handler: Arc::new(test_utils::TestRoutingMessageHandler::new()), onion_message_handler: IgnoringMessageHandler {}, custom_message_handler: Arc::clone(&liquidity_manager), diff --git a/lightning-net-tokio/src/lib.rs b/lightning-net-tokio/src/lib.rs index a0daa8235b5..be4b7f0e4dd 100644 --- a/lightning-net-tokio/src/lib.rs +++ b/lightning-net-tokio/src/lib.rs @@ -832,6 +832,7 @@ mod tests { route_handler: Arc::clone(&a_handler), onion_message_handler: Arc::new(IgnoringMessageHandler {}), custom_message_handler: Arc::new(IgnoringMessageHandler {}), + send_only_message_handler: Arc::new(IgnoringMessageHandler {}), }; let a_manager = Arc::new(PeerManager::new( a_msg_handler, @@ -855,6 +856,7 @@ mod tests { route_handler: Arc::clone(&b_handler), onion_message_handler: Arc::new(IgnoringMessageHandler {}), custom_message_handler: Arc::new(IgnoringMessageHandler {}), + send_only_message_handler: Arc::new(IgnoringMessageHandler {}), }; let b_manager = Arc::new(PeerManager::new( b_msg_handler, @@ -917,6 +919,7 @@ mod tests { onion_message_handler: Arc::new(IgnoringMessageHandler {}), route_handler: Arc::new(lightning::ln::peer_handler::IgnoringMessageHandler {}), custom_message_handler: Arc::new(IgnoringMessageHandler {}), + send_only_message_handler: Arc::new(IgnoringMessageHandler {}), }; let a_manager = Arc::new(PeerManager::new( a_msg_handler, diff --git a/lightning/src/chain/chainmonitor.rs b/lightning/src/chain/chainmonitor.rs index b953b386ed6..d32468b3238 100644 --- a/lightning/src/chain/chainmonitor.rs +++ b/lightning/src/chain/chainmonitor.rs @@ -32,6 +32,8 @@ use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator}; use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, Balance, MonitorEvent, TransactionOutputs, WithChannelMonitor}; use crate::chain::transaction::{OutPoint, TransactionData}; use crate::ln::types::ChannelId; +use crate::ln::msgs::{self, BaseMessageHandler, Init, MessageSendEvent}; +use crate::ln::our_peer_storage::OurPeerStorage; use crate::sign::ecdsa::EcdsaChannelSigner; use crate::events::{self, Event, EventHandler, ReplayEvent}; use crate::util::logger::{Logger, WithContext}; @@ -39,9 +41,9 @@ use crate::util::errors::APIError; use crate::util::persist::MonitorName; use crate::util::wakers::{Future, Notifier}; use crate::ln::channel_state::ChannelDetails; - use crate::prelude::*; use crate::sync::{RwLock, RwLockReadGuard, Mutex, MutexGuard}; +use crate::types::features::{InitFeatures, NodeFeatures}; use core::ops::Deref; use core::sync::atomic::{AtomicUsize, Ordering}; use bitcoin::secp256k1::PublicKey; @@ -253,6 +255,9 @@ pub struct ChainMonitor>, + + our_peerstorage_encryption_key: [u8; 32], } impl ChainMonitor @@ -386,7 +391,15 @@ where C::Target: chain::Filter, /// pre-filter blocks or only fetch blocks matching a compact filter. Otherwise, clients may /// always need to fetch full blocks absent another means for determining which blocks contain /// transactions relevant to the watched channels. - pub fn new(chain_source: Option, broadcaster: T, logger: L, feeest: F, persister: P) -> Self { + /// + /// # Note + /// `our_peerstorage_encryption_key` must be obtained from [`crate::sign::NodeSigner::get_peer_storage_key()`]. + /// This key is used to encrypt peer storage backups. + /// + /// **Important**: This key should not be set arbitrarily or changed after initialization. The same key + /// is obtained by the `ChannelManager` through `KeyMananger` to decrypt peer backups. + /// Using an inconsistent or incorrect key will result in the inability to decrypt previously encrypted backups. + pub fn new(chain_source: Option, broadcaster: T, logger: L, feeest: F, persister: P, our_peerstorage_encryption_key: [u8; 32]) -> Self { Self { monitors: RwLock::new(new_hash_map()), chain_source, @@ -397,6 +410,8 @@ where C::Target: chain::Filter, pending_monitor_events: Mutex::new(Vec::new()), highest_chain_height: AtomicUsize::new(0), event_notifier: Notifier::new(), + pending_send_only_events: Mutex::new(Vec::new()), + our_peerstorage_encryption_key } } @@ -665,6 +680,52 @@ where C::Target: chain::Filter, }); } } + + /// Retrieves all node IDs associated with the monitors. + /// + /// This function collects the counterparty node IDs from all monitors into a `HashSet`, + /// ensuring unique IDs are returned. + fn get_peer_node_ids(&self) -> HashSet { + let mon = self.monitors.read().unwrap(); + mon + .values() + .map(|monitor| monitor.monitor.get_counterparty_node_id()) + .collect() + } + + fn send_peer_storage(&self, their_node_id: PublicKey) { + // TODO: Serialize `ChannelMonitor`s inside `our_peer_storage`. + + let our_peer_storage = OurPeerStorage::create_from_data(self.our_peerstorage_encryption_key, Vec::new()); + log_debug!(self.logger, "Sending Peer Storage from chainmonitor"); + self.pending_send_only_events.lock().unwrap().push(MessageSendEvent::SendPeerStorage { node_id: their_node_id, + msg: msgs::PeerStorage { data: our_peer_storage.encrypted_data() } }) + } +} + +impl BaseMessageHandler for ChainMonitor +where C::Target: chain::Filter, + T::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, + P::Target: Persist, +{ + fn get_and_clear_pending_msg_events(&self) -> Vec { + let mut pending_events = self.pending_send_only_events.lock().unwrap(); + core::mem::take(&mut *pending_events) + } + + fn peer_disconnected(&self, _their_node_id: PublicKey) {} + + fn provided_node_features(&self) -> NodeFeatures { + NodeFeatures::empty() + } + + fn provided_init_features(&self, _their_node_id: PublicKey) -> InitFeatures { + InitFeatures::empty() + } + + fn peer_connected(&self, _their_node_id: PublicKey, _msg: &Init, _inbound: bool) -> Result<(), ()> { Ok(()) } } impl @@ -682,6 +743,12 @@ where monitor.block_connected( header, txdata, height, &*self.broadcaster, &*self.fee_estimator, &self.logger) }); + + // Send peer storage everytime a new block arrives. + for node_id in self.get_peer_node_ids() { + self.send_peer_storage(node_id); + } + // Assume we may have some new events and wake the event processor self.event_notifier.notify(); } @@ -733,6 +800,12 @@ where header, height, &*self.broadcaster, &*self.fee_estimator, &self.logger ) }); + + // Send peer storage everytime a new block arrives. + for node_id in self.get_peer_node_ids() { + self.send_peer_storage(node_id); + } + // Assume we may have some new events and wake the event processor self.event_notifier.notify(); } diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index 89b83ede3eb..da853def77c 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -1609,6 +1609,7 @@ fn route_blinding_spec_test_vector() { fn sign_invoice( &self, _invoice: &RawBolt11Invoice, _recipient: Recipient, ) -> Result { unreachable!() } + fn get_peer_storage_key(&self) -> [u8; 32] { unreachable!() } fn sign_bolt12_invoice( &self, _invoice: &UnsignedBolt12Invoice, ) -> Result { unreachable!() } @@ -1918,6 +1919,7 @@ fn test_trampoline_inbound_payment_decoding() { fn sign_invoice( &self, _invoice: &RawBolt11Invoice, _recipient: Recipient, ) -> Result { unreachable!() } + fn get_peer_storage_key(&self) -> [u8; 32] { unreachable!() } fn sign_bolt12_invoice( &self, _invoice: &UnsignedBolt12Invoice, ) -> Result { unreachable!() } diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index c69e6c6b02b..ad91e40c93a 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -52,6 +52,7 @@ use crate::ln::types::ChannelId; use crate::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret}; use crate::ln::channel::{self, Channel, ChannelError, ChannelUpdateStatus, FundedChannel, ShutdownResult, UpdateFulfillCommitFetch, OutboundV1Channel, ReconnectionMsg, InboundV1Channel, WithChannelContext}; use crate::ln::channel::PendingV2Channel; +use crate::ln::our_peer_storage::OurPeerStorage; use crate::ln::channel_state::ChannelDetails; use crate::types::features::{Bolt12InvoiceFeatures, ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures}; #[cfg(any(feature = "_test_utils", test))] @@ -77,8 +78,8 @@ use crate::onion_message::async_payments::{AsyncPaymentsMessage, HeldHtlcAvailab use crate::onion_message::dns_resolution::HumanReadableName; use crate::onion_message::messenger::{Destination, MessageRouter, Responder, ResponseInstruction, MessageSendInstructions}; use crate::onion_message::offers::{OffersMessage, OffersMessageHandler}; -use crate::sign::{EntropySource, NodeSigner, Recipient, SignerProvider}; use crate::sign::ecdsa::EcdsaChannelSigner; +use crate::sign::{EntropySource, NodeSigner, Recipient, SignerProvider}; use crate::util::config::{ChannelConfig, ChannelConfigUpdate, ChannelConfigOverrides, UserConfig}; use crate::util::wakers::{Future, Notifier}; use crate::util::scid_utils::fake_scid; @@ -8341,15 +8342,40 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } } - fn internal_peer_storage_retrieval(&self, counterparty_node_id: PublicKey, _msg: msgs::PeerStorageRetrieval) -> Result<(), MsgHandleErrInternal> { - // TODO: Decrypt and check if have any stale or missing ChannelMonitor. + fn internal_peer_storage_retrieval(&self, counterparty_node_id: PublicKey, msg: msgs::PeerStorageRetrieval) -> Result<(), MsgHandleErrInternal> { + // TODO: Check if have any stale or missing ChannelMonitor. let logger = WithContext::from(&self.logger, Some(counterparty_node_id), None, None); - log_debug!(logger, "Received unexpected peer_storage_retrieval from {}. This is unusual since we do not yet distribute peer storage. Sending a warning.", log_pubkey!(counterparty_node_id)); + // `MIN_CYPHERTEXT_LEN` is 16 bytes because the mandatory authentication tag length is 16 bytes. + const MIN_CYPHERTEXT_LEN: usize = 16; + + if msg.data.len() < MIN_CYPHERTEXT_LEN { + log_debug!(logger, "Invalid YourPeerStorage received from {}", log_pubkey!(counterparty_node_id)); + return Err(MsgHandleErrInternal::from_chan_no_close(ChannelError::Warn( + "Invalid peer_storage_retrieval message received.".into(), + ), ChannelId([0; 32]))); + } + + let our_peerstorage_encryption_key = self.node_signer.get_peer_storage_key(); + let our_peer_storage = OurPeerStorage::new(msg.data); + + match our_peer_storage.decrypt_our_peer_storage(our_peerstorage_encryption_key) { + Ok(decrypted_data) => { + // Decryption successful. + if decrypted_data.len() == 0 { + log_trace!(logger, "Received a peer storage from peer {} with 0 channels.", log_pubkey!(counterparty_node_id)); + } + } + Err(_) => { + log_debug!(logger, "Invalid YourPeerStorage received from {}", log_pubkey!(counterparty_node_id)); + + return Err(MsgHandleErrInternal::from_chan_no_close(ChannelError::Ignore( + "Invalid peer_storage_retrieval message received.".into(), + ), ChannelId([0; 32]))); + } + } - Err(MsgHandleErrInternal::from_chan_no_close(ChannelError::Warn( - "Invalid peer_storage_retrieval message received.".into(), - ), ChannelId([0; 32]))) + Ok(()) } fn internal_peer_storage(&self, counterparty_node_id: PublicKey, msg: msgs::PeerStorage) -> Result<(), MsgHandleErrInternal> { @@ -15174,9 +15200,26 @@ mod tests { create_announced_chan_between_nodes(&nodes, 0, 1); - // Since we do not send peer storage, we manually simulate receiving a dummy - // `PeerStorage` from the channel partner. - nodes[0].node.handle_peer_storage(nodes[1].node.get_our_node_id(), msgs::PeerStorage{data: vec![0; 100]}); + let peer_storage_msg_events_node0 = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_msg_events(); + let peer_storage_msg_events_node1 = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_msg_events(); + assert_ne!(peer_storage_msg_events_node0.len(), 0); + assert_ne!(peer_storage_msg_events_node1.len(), 0); + + match peer_storage_msg_events_node0[0] { + MessageSendEvent::SendPeerStorage { ref node_id, ref msg } => { + assert_eq!(*node_id, nodes[1].node.get_our_node_id()); + nodes[1].node.handle_peer_storage(nodes[0].node.get_our_node_id(), msg.clone()); + } + _ => panic!("Unexpected event"), + } + + match peer_storage_msg_events_node1[0] { + MessageSendEvent::SendPeerStorage { ref node_id, ref msg } => { + assert_eq!(*node_id, nodes[0].node.get_our_node_id()); + nodes[0].node.handle_peer_storage(nodes[1].node.get_our_node_id(), msg.clone()); + } + _ => panic!("Unexpected event"), + } nodes[0].node.peer_disconnected(nodes[1].node.get_our_node_id()); nodes[1].node.peer_disconnected(nodes[0].node.get_our_node_id()); @@ -15188,9 +15231,24 @@ mod tests { features: nodes[0].node.init_features(), networks: None, remote_network_address: None }, false).unwrap(); + let node_1_events = nodes[1].node.get_and_clear_pending_msg_events(); + assert_eq!(node_1_events.len(), 2); + let node_0_events = nodes[0].node.get_and_clear_pending_msg_events(); assert_eq!(node_0_events.len(), 2); + for msg in node_1_events{ + if let MessageSendEvent::SendChannelReestablish { ref node_id, ref msg } = msg { + nodes[0].node.handle_channel_reestablish(nodes[1].node.get_our_node_id(), msg); + assert_eq!(*node_id, nodes[0].node.get_our_node_id()); + } else if let MessageSendEvent::SendPeerStorageRetrieval { ref node_id, ref msg } = msg { + nodes[0].node.handle_peer_storage_retrieval(nodes[1].node.get_our_node_id(), msg.clone()); + assert_eq!(*node_id, nodes[0].node.get_our_node_id()); + } else { + panic!("Unexpected event") + } + } + for msg in node_0_events{ if let MessageSendEvent::SendChannelReestablish { ref node_id, ref msg } = msg { nodes[1].node.handle_channel_reestablish(nodes[0].node.get_our_node_id(), msg); @@ -15203,30 +15261,9 @@ mod tests { } } - let msg_events_after_peer_storage_retrieval = nodes[1].node.get_and_clear_pending_msg_events(); - - // Check if we receive a warning message. - let peer_storage_warning: Vec<&MessageSendEvent> = msg_events_after_peer_storage_retrieval - .iter() - .filter(|event| match event { - MessageSendEvent::HandleError { .. } => true, - _ => false, - }) - .collect(); - - assert_eq!(peer_storage_warning.len(), 1); - - match peer_storage_warning[0] { - MessageSendEvent::HandleError { node_id, action } => { - assert_eq!(*node_id, nodes[0].node.get_our_node_id()); - match action { - ErrorAction::SendWarningMessage { msg, .. } => - assert_eq!(msg.data, "Invalid peer_storage_retrieval message received.".to_owned()), - _ => panic!("Unexpected error action"), - } - } - _ => panic!("Unexpected event"), - } + // Clear all other messages. + nodes[1].node.get_and_clear_pending_msg_events(); + nodes[0].node.get_and_clear_pending_msg_events(); } #[test] @@ -16323,7 +16360,7 @@ mod tests { pub mod bench { use crate::chain::Listen; use crate::chain::chainmonitor::{ChainMonitor, Persist}; - use crate::sign::{KeysManager, InMemorySigner}; + use crate::sign::{KeysManager, InMemorySigner, NodeSigner}; use crate::events::Event; use crate::ln::channelmanager::{BestBlock, ChainParameters, ChannelManager, PaymentHash, PaymentPreimage, PaymentId, RecipientOnionFields, Retry}; use crate::ln::functional_test_utils::*; @@ -16386,9 +16423,9 @@ pub mod bench { config.channel_config.max_dust_htlc_exposure = MaxDustHTLCExposure::FeeRateMultiplier(5_000_000 / 253); config.channel_handshake_config.minimum_depth = 1; - let chain_monitor_a = ChainMonitor::new(None, &tx_broadcaster, &logger_a, &fee_estimator, &persister_a); let seed_a = [1u8; 32]; let keys_manager_a = KeysManager::new(&seed_a, 42, 42); + let chain_monitor_a = ChainMonitor::new(None, &tx_broadcaster, &logger_a, &fee_estimator, &persister_a, keys_manager_a.get_peer_storage_key()); let node_a = ChannelManager::new(&fee_estimator, &chain_monitor_a, &tx_broadcaster, &router, &message_router, &logger_a, &keys_manager_a, &keys_manager_a, &keys_manager_a, config.clone(), ChainParameters { network, best_block: BestBlock::from_network(network), @@ -16396,9 +16433,9 @@ pub mod bench { let node_a_holder = ANodeHolder { node: &node_a }; let logger_b = test_utils::TestLogger::with_id("node a".to_owned()); - let chain_monitor_b = ChainMonitor::new(None, &tx_broadcaster, &logger_a, &fee_estimator, &persister_b); let seed_b = [2u8; 32]; let keys_manager_b = KeysManager::new(&seed_b, 42, 42); + let chain_monitor_b = ChainMonitor::new(None, &tx_broadcaster, &logger_a, &fee_estimator, &persister_b, keys_manager_b.get_peer_storage_key()); let node_b = ChannelManager::new(&fee_estimator, &chain_monitor_b, &tx_broadcaster, &router, &message_router, &logger_b, &keys_manager_b, &keys_manager_b, &keys_manager_b, config.clone(), ChainParameters { network, best_block: BestBlock::from_network(network), diff --git a/lightning/src/ln/mod.rs b/lightning/src/ln/mod.rs index dc87200d300..7849422c351 100644 --- a/lightning/src/ln/mod.rs +++ b/lightning/src/ln/mod.rs @@ -24,6 +24,7 @@ pub mod chan_utils; mod features; pub mod script; pub mod types; +pub mod our_peer_storage; // TODO: These modules were moved from lightning-invoice and need to be better integrated into this // crate now: diff --git a/lightning/src/ln/our_peer_storage.rs b/lightning/src/ln/our_peer_storage.rs new file mode 100644 index 00000000000..101862d9576 --- /dev/null +++ b/lightning/src/ln/our_peer_storage.rs @@ -0,0 +1,109 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +//! `OurPeerStorage` enables versioned storage of serialized channel data. +//! It supports encryption and decryption to maintain data integrity and security during +//! transmission. + +use crate::crypto::chacha20poly1305rfc::ChaCha20Poly1305RFC; +use crate::prelude::*; + +/// [`OurPeerStorage`] is used to store channel information that allows for the creation of a +/// `peer_storage` backup. It includes versioning and timestamping for comparison between +/// instances of [`OurPeerStorage`]. +/// +/// This structure is designed to serialize channel data for backup and supports encryption +/// and decryption to ensure data integrity and security during exchange or storage. +/// +/// # Key Methods +/// - `create_from_data`: Returns an encrypted [`OurPeerStorage`] instance created from the provided data. +/// - `decrypt_our_peer_storage`: Decrypts the [`OurPeerStorage::encrypted_data`] using the key and returns decrypted data. +/// +/// # Usage +/// This structure can be used for securely managing and exchanging peer storage backups. It +/// includes methods for encryption and decryption using `ChaCha20Poly1305RFC`, making it +/// suitable for on-the-wire transmission. +/// +/// ## Example +/// ```ignore +/// let key = [0u8; 32]; +/// let our_peer_storage = OurPeerStorage::create_from_data(key.clone(), vec![1,2,3]); +/// let decrypted_data = our_peer_storage.decrypt_our_peer_storage(key).unwrap(); +/// assert_eq!(decrypted_data, vec![1 , 2, 3]); +/// ``` +#[derive(PartialEq)] +pub struct OurPeerStorage { + encrypted_data: Vec, +} + +impl OurPeerStorage { + /// Creates a new [`OurPeerStorage`] with given encrypted_data. + pub fn new(encrypted_data: Vec) -> Self { + Self { encrypted_data } + } + + /// Get encrypted data stored inside [`OurPeerStorage`]. + pub fn encrypted_data(&self) -> Vec { + self.encrypted_data.clone() + } + + /// Creates a serialised representation of [`OurPeerStorage`] from the given `ser_channels` data. + /// + /// This function takes a `key` (for encryption) and `ser_channels` data + /// (serialised channel information), and returns a serialised [`OurPeerStorage`] as a `Vec`. + /// + /// The resulting serialised data is intended to be directly used for transmission to the peers. + pub fn create_from_data(key: [u8; 32], ser_channels: Vec) -> OurPeerStorage { + let n = 0u64; + + let mut res = vec![0; ser_channels.len() + 16]; + + let plaintext = &ser_channels[..]; + let mut nonce = [0; 12]; + nonce[4..].copy_from_slice(&n.to_le_bytes()[..]); + + let mut chacha = ChaCha20Poly1305RFC::new(&key, &nonce, b""); + let mut tag = [0; 16]; + chacha.encrypt(plaintext, &mut res[0..plaintext.len()], &mut tag); + res[plaintext.len()..].copy_from_slice(&tag); + + Self { encrypted_data: res } + } + + /// Decrypt `OurPeerStorage` using the `key`, result is stored inside the `res`. + /// Returns an error if the the `cyphertext` is not correct. + pub fn decrypt_our_peer_storage(self, key: [u8; 32]) -> Result, ()> { + const MIN_CYPHERTEXT_LEN: usize = 16; + let cyphertext = &self.encrypted_data[..]; + + let mut res = vec![0; cyphertext.len() - 16]; + // Ensure the cyphertext is at least as large as the MIN_CYPHERTEXT_LEN. + if cyphertext.len() < MIN_CYPHERTEXT_LEN { + return Err(()); + } + + let n = 0u64; + let mut nonce = [0; 12]; + nonce[4..].copy_from_slice(&n.to_le_bytes()[..]); + + let mut chacha = ChaCha20Poly1305RFC::new(&key, &nonce, b""); + if chacha + .variable_time_decrypt( + &cyphertext[0..cyphertext.len() - 16], + &mut res, + &cyphertext[cyphertext.len() - 16..], + ) + .is_err() + { + return Err(()); + } + + Ok(res) + } +} diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index 336ce40cf11..73132841d49 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -49,10 +49,11 @@ use core::ops::Deref; use core::convert::Infallible; #[cfg(not(c_bindings))] use { + crate::chain::chainmonitor::ChainMonitor, crate::ln::channelmanager::{SimpleArcChannelManager, SimpleRefChannelManager}, crate::onion_message::messenger::{SimpleArcOnionMessenger, SimpleRefOnionMessenger}, crate::routing::gossip::{NetworkGraph, P2PGossipSync}, - crate::sign::KeysManager, + crate::sign::{KeysManager, InMemorySigner}, crate::sync::Arc, }; @@ -411,11 +412,12 @@ impl Deref for ErroringMessageHandler { } /// Provides references to trait impls which handle different types of messages. -pub struct MessageHandler where +pub struct MessageHandler where CM::Target: ChannelMessageHandler, RM::Target: RoutingMessageHandler, OM::Target: OnionMessageHandler, CustomM::Target: CustomMessageHandler, + SM::Target: BaseMessageHandler, { /// A message handler which handles messages specific to channels. Usually this is just a /// [`ChannelManager`] object or an [`ErroringMessageHandler`]. @@ -437,6 +439,12 @@ pub struct MessageHandler where /// A message handler which handles custom messages. The only LDK-provided implementation is /// [`IgnoringMessageHandler`]. pub custom_message_handler: CustomM, + + /// A message handler which only allows sending messages. This should generally be a + /// [`ChainMonitor`]. + /// + /// [`ChainMonitor`]: crate::chain::chainmonitor::ChainMonitor + pub send_only_message_handler: SM, } /// Provides an object which can be used to send data to and which uniquely identifies a connection @@ -705,14 +713,15 @@ impl Peer { /// /// This is not exported to bindings users as type aliases aren't supported in most languages. #[cfg(not(c_bindings))] -pub type SimpleArcPeerManager = PeerManager< +pub type SimpleArcPeerManager = PeerManager< SD, Arc>, Arc>>, C, Arc>>, Arc>, Arc, IgnoringMessageHandler, - Arc + Arc, + Arc, Arc, Arc, Arc, Arc>>, >; /// SimpleRefPeerManager is a type alias for a PeerManager reference, and is the reference @@ -733,7 +742,8 @@ pub type SimpleRefPeerManager< &'h SimpleRefOnionMessenger<'a, 'b, 'c, 'd, 'e, 'graph, 'logger, 'i, 'j, 'k, M, T, F, L>, &'logger L, IgnoringMessageHandler, - &'c KeysManager + &'c KeysManager, + &'j ChainMonitor<&'a M, C, &'b T, &'c F, &'logger L, &'c KeysManager>, >; @@ -758,18 +768,21 @@ pub trait APeerManager { type CMH: Deref; type NST: NodeSigner + ?Sized; type NS: Deref; + type SMT: BaseMessageHandler + ?Sized; + type SM: Deref; /// Gets a reference to the underlying [`PeerManager`]. - fn as_ref(&self) -> &PeerManager; + fn as_ref(&self) -> &PeerManager; } -impl -APeerManager for PeerManager where +impl +APeerManager for PeerManager where CM::Target: ChannelMessageHandler, RM::Target: RoutingMessageHandler, OM::Target: OnionMessageHandler, L::Target: Logger, CMH::Target: CustomMessageHandler, NS::Target: NodeSigner, + SM::Target: BaseMessageHandler, { type Descriptor = Descriptor; type CMT = ::Target; @@ -784,7 +797,9 @@ APeerManager for PeerManager where type CMH = CMH; type NST = ::Target; type NS = NS; - fn as_ref(&self) -> &PeerManager { self } + type SMT = ::Target; + type SM = SM; + fn as_ref(&self) -> &PeerManager { self } } /// A PeerManager manages a set of peers, described by their [`SocketDescriptor`] and marshalls @@ -806,14 +821,16 @@ APeerManager for PeerManager where /// you're using lightning-net-tokio. /// /// [`read_event`]: PeerManager::read_event -pub struct PeerManager where +pub struct PeerManager where CM::Target: ChannelMessageHandler, RM::Target: RoutingMessageHandler, OM::Target: OnionMessageHandler, L::Target: Logger, CMH::Target: CustomMessageHandler, - NS::Target: NodeSigner { - message_handler: MessageHandler, + NS::Target: NodeSigner, + SM::Target: BaseMessageHandler, +{ + message_handler: MessageHandler, /// Connection state for each connected peer - we have an outer read-write lock which is taken /// as read while we're doing processing for a peer and taken write when a peer is being added /// or removed. @@ -883,11 +900,13 @@ macro_rules! encode_msg { }} } -impl PeerManager where +impl PeerManager where CM::Target: ChannelMessageHandler, OM::Target: OnionMessageHandler, L::Target: Logger, - NS::Target: NodeSigner { + NS::Target: NodeSigner, + SM::Target: BaseMessageHandler, +{ /// Constructs a new `PeerManager` with the given `ChannelMessageHandler` and /// `OnionMessageHandler`. No routing message handler is used and network graph messages are /// ignored. @@ -901,17 +920,18 @@ impl Pe /// minute should suffice. /// /// This is not exported to bindings users as we can't export a PeerManager with a dummy route handler - pub fn new_channel_only(channel_message_handler: CM, onion_message_handler: OM, current_time: u32, ephemeral_random_data: &[u8; 32], logger: L, node_signer: NS) -> Self { + pub fn new_channel_only(channel_message_handler: CM, onion_message_handler: OM, current_time: u32, ephemeral_random_data: &[u8; 32], logger: L, node_signer: NS, send_only_message_handler: SM) -> Self { Self::new(MessageHandler { chan_handler: channel_message_handler, route_handler: IgnoringMessageHandler{}, onion_message_handler, custom_message_handler: IgnoringMessageHandler{}, + send_only_message_handler, }, current_time, ephemeral_random_data, logger, node_signer) } } -impl PeerManager where +impl PeerManager where RM::Target: RoutingMessageHandler, L::Target: Logger, NS::Target: NodeSigner { @@ -935,6 +955,7 @@ impl PeerManager) -> Option } } -impl PeerManager where +impl PeerManager where CM::Target: ChannelMessageHandler, RM::Target: RoutingMessageHandler, OM::Target: OnionMessageHandler, L::Target: Logger, CMH::Target: CustomMessageHandler, - NS::Target: NodeSigner + NS::Target: NodeSigner, + SM::Target: BaseMessageHandler, { /// Constructs a new `PeerManager` with the given message handlers. /// @@ -997,7 +1019,7 @@ impl, current_time: u32, ephemeral_random_data: &[u8; 32], logger: L, node_signer: NS) -> Self { + pub fn new(message_handler: MessageHandler, current_time: u32, ephemeral_random_data: &[u8; 32], logger: L, node_signer: NS) -> Self { let mut ephemeral_key_midstate = Sha256::engine(); ephemeral_key_midstate.input(ephemeral_random_data); @@ -2494,6 +2516,11 @@ impl(peer_count: usize, cfgs: &'a Vec) -> Vec> { + fn create_network<'a>(peer_count: usize, cfgs: &'a Vec) -> Vec> { let mut peers = Vec::new(); for i in 0..peer_count { let ephemeral_bytes = [i as u8; 32]; let msg_handler = MessageHandler { chan_handler: &cfgs[i].chan_handler, route_handler: &cfgs[i].routing_handler, - onion_message_handler: IgnoringMessageHandler {}, custom_message_handler: &cfgs[i].custom_handler + onion_message_handler: IgnoringMessageHandler {}, custom_message_handler: &cfgs[i].custom_handler, send_only_message_handler: IgnoringMessageHandler {}, }; let peer = PeerManager::new(msg_handler, 0, &ephemeral_bytes, &cfgs[i].logger, &cfgs[i].node_signer); peers.push(peer); @@ -3006,7 +3033,7 @@ mod tests { peers } - fn try_establish_connection<'a>(peer_a: &PeerManager, peer_b: &PeerManager) -> (FileDescriptor, FileDescriptor, Result, Result) { + fn try_establish_connection<'a>(peer_a: &PeerManager, peer_b: &PeerManager) -> (FileDescriptor, FileDescriptor, Result, Result) { let addr_a = SocketAddress::TcpIpV4{addr: [127, 0, 0, 1], port: 1000}; let addr_b = SocketAddress::TcpIpV4{addr: [127, 0, 0, 1], port: 1001}; @@ -3037,7 +3064,7 @@ mod tests { } - fn establish_connection<'a>(peer_a: &PeerManager, peer_b: &PeerManager) -> (FileDescriptor, FileDescriptor) { + fn establish_connection<'a>(peer_a: &PeerManager, peer_b: &PeerManager) -> (FileDescriptor, FileDescriptor) { let addr_a = SocketAddress::TcpIpV4{addr: [127, 0, 0, 1], port: 1000}; let addr_b = SocketAddress::TcpIpV4{addr: [127, 0, 0, 1], port: 1001}; @@ -3439,12 +3466,14 @@ mod tests { route_handler: IgnoringMessageHandler {}, onion_message_handler: IgnoringMessageHandler {}, custom_message_handler: IgnoringMessageHandler {}, + send_only_message_handler: IgnoringMessageHandler {}, }, 0, &[0; 32], &logger, &node_signer_a); let peer_b = PeerManager::new(MessageHandler { chan_handler: ErroringMessageHandler::new(), route_handler: IgnoringMessageHandler {}, onion_message_handler: IgnoringMessageHandler {}, custom_message_handler: IgnoringMessageHandler {}, + send_only_message_handler: IgnoringMessageHandler {}, }, 0, &[1; 32], &logger, &node_signer_b); let a_id = node_signer_a.get_node_id(Recipient::Node).unwrap(); diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs index 0539a696852..db46cd2b21f 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -829,6 +829,15 @@ pub trait NodeSigner { /// [phantom node payments]: PhantomKeysManager fn get_inbound_payment_key(&self) -> ExpandedKey; + /// Defines a method to derive a 32-byte encryption key for peer storage. + /// + /// Implementations of this method must derive a secure encryption key. + /// The key is used to encrypt or decrypt backups of our state stored with our peers. + /// + /// Thus, if you wish to rely on recovery using this method, you should use a key which + /// can be re-derived from data which would be available after state loss (eg the wallet seed) + fn get_peer_storage_key(&self) -> [u8; 32]; + /// Get node id based on the provided [`Recipient`]. /// /// This method must return the same value each time it is called with a given [`Recipient`] @@ -1778,6 +1787,7 @@ pub struct KeysManager { shutdown_pubkey: PublicKey, channel_master_key: Xpriv, channel_child_index: AtomicUsize, + peer_storage_key: SecretKey, #[cfg(test)] pub(crate) entropy_source: RandomBytes, @@ -1846,6 +1856,10 @@ impl KeysManager { .private_key; let mut inbound_pmt_key_bytes = [0; 32]; inbound_pmt_key_bytes.copy_from_slice(&inbound_payment_key[..]); + let peer_storage_key: SecretKey = master_key + .derive_priv(&secp_ctx, &ChildNumber::from_hardened_idx(6).unwrap()) + .expect("Your RNG is busted") + .private_key; let mut rand_bytes_engine = Sha256::engine(); rand_bytes_engine.input(&starting_time_secs.to_be_bytes()); @@ -1861,6 +1875,8 @@ impl KeysManager { node_id, inbound_payment_key: ExpandedKey::new(inbound_pmt_key_bytes), + peer_storage_key, + destination_script, shutdown_pubkey, @@ -2086,6 +2102,10 @@ impl NodeSigner for KeysManager { self.inbound_payment_key.clone() } + fn get_peer_storage_key(&self) -> [u8; 32] { + self.peer_storage_key.secret_bytes() + } + fn sign_invoice( &self, invoice: &RawBolt11Invoice, recipient: Recipient, ) -> Result { @@ -2247,6 +2267,10 @@ impl NodeSigner for PhantomKeysManager { self.inbound_payment_key.clone() } + fn get_peer_storage_key(&self) -> [u8; 32] { + self.inner.peer_storage_key.secret_bytes() + } + fn sign_invoice( &self, invoice: &RawBolt11Invoice, recipient: Recipient, ) -> Result { diff --git a/lightning/src/util/dyn_signer.rs b/lightning/src/util/dyn_signer.rs index e08d7a1f0d0..4bda5c63f59 100644 --- a/lightning/src/util/dyn_signer.rs +++ b/lightning/src/util/dyn_signer.rs @@ -220,7 +220,8 @@ inner, fn sign_bolt12_invoice(, invoice: &crate::offers::invoice::UnsignedBolt12Invoice ) -> Result, - fn get_inbound_payment_key(,) -> ExpandedKey + fn get_inbound_payment_key(,) -> ExpandedKey, + fn get_peer_storage_key(,) -> [u8; 32] ); delegate!(DynKeysInterface, SignerProvider, @@ -288,7 +289,8 @@ delegate!(DynPhantomKeysInterface, NodeSigner, fn sign_invoice(, invoice: &RawBolt11Invoice, recipient: Recipient) -> Result, fn sign_bolt12_invoice(, invoice: &crate::offers::invoice::UnsignedBolt12Invoice ) -> Result, - fn get_inbound_payment_key(,) -> ExpandedKey + fn get_inbound_payment_key(,) -> ExpandedKey, + fn get_peer_storage_key(,) -> [u8; 32] ); impl SignerProvider for DynPhantomKeysInterface { diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 501207e1e22..cb98ad6cc81 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -416,6 +416,7 @@ impl<'a> TestChainMonitor<'a> { logger, fee_estimator, persister, + keys_manager.get_peer_storage_key(), ), keys_manager, expect_channel_force_closed: Mutex::new(None), @@ -1451,6 +1452,10 @@ impl NodeSigner for TestNodeSigner { unreachable!() } + fn get_peer_storage_key(&self) -> [u8; 32] { + unreachable!() + } + fn get_node_id(&self, recipient: Recipient) -> Result { let node_secret = match recipient { Recipient::Node => Ok(&self.node_secret), @@ -1529,6 +1534,10 @@ impl NodeSigner for TestKeysInterface { self.backing.sign_invoice(invoice, recipient) } + fn get_peer_storage_key(&self) -> [u8; 32] { + self.backing.get_peer_storage_key() + } + fn sign_bolt12_invoice( &self, invoice: &UnsignedBolt12Invoice, ) -> Result {