diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 1869dfb0d1c..4da2e99fb1a 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -62,7 +62,9 @@ use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessa use lightning::routing::router::{ InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters, Router, }; -use lightning::sign::{EntropySource, InMemorySigner, NodeSigner, Recipient, SignerProvider}; +use lightning::sign::{ + EntropySource, InMemorySigner, NodeSigner, PeerStorageKey, Recipient, SignerProvider, +}; use lightning::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret}; use lightning::util::config::UserConfig; use lightning::util::hash_tables::*; @@ -204,6 +206,7 @@ impl TestChainMonitor { logger.clone(), feeest, Arc::clone(&persister), + keys.get_peer_storage_key(), )), logger, keys, @@ -337,6 +340,10 @@ impl NodeSigner for KeyProvider { unreachable!() } + fn get_peer_storage_key(&self) -> PeerStorageKey { + PeerStorageKey::new([42; 32]) + } + fn sign_bolt12_invoice( &self, _invoice: &UnsignedBolt12Invoice, ) -> Result { diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index a2f4ecac227..7ec06a633c6 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -34,7 +34,7 @@ use lightning::blinded_path::message::{BlindedMessagePath, MessageContext}; use lightning::blinded_path::payment::{BlindedPaymentPath, ReceiveTlvs}; use lightning::chain; use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; -use lightning::chain::chainmonitor; +use lightning::chain::chainmonitor::{self, PeerStorageKey}; use lightning::chain::transaction::OutPoint; use lightning::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen}; use lightning::events::Event; @@ -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) -> PeerStorageKey { + PeerStorageKey::new([42; 32]) + } } 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 a5782dacd42..36efc74453b 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -9,6 +9,7 @@ use lightning::blinded_path::message::{ AsyncPaymentsContext, BlindedMessagePath, MessageContext, OffersContext, }; use lightning::blinded_path::EmptyNodeIdLookUp; +use lightning::chain::chainmonitor::PeerStorageKey; use lightning::ln::inbound_payment::ExpandedKey; use lightning::ln::msgs::{self, BaseMessageHandler, DecodeError, OnionMessageHandler}; use lightning::ln::peer_handler::IgnoringMessageHandler; @@ -249,6 +250,10 @@ impl NodeSigner for KeyProvider { ) -> Result { unreachable!() } + + fn get_peer_storage_key(&self) -> PeerStorageKey { + unreachable!() + } } impl SignerProvider for KeyProvider { diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs index 46d990bb37e..25304fdaeb3 100644 --- a/lightning-background-processor/src/lib.rs +++ b/lightning-background-processor/src/lib.rs @@ -36,6 +36,7 @@ use lightning::onion_message::messenger::AOnionMessenger; use lightning::routing::gossip::{NetworkGraph, P2PGossipSync}; use lightning::routing::scoring::{ScoreUpdate, WriteableScore}; use lightning::routing::utxo::UtxoLookup; +use lightning::sign::EntropySource; use lightning::util::logger::Logger; use lightning::util::persist::Persister; #[cfg(feature = "std")] @@ -649,7 +650,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, @@ -736,8 +737,9 @@ pub async fn process_events_async< EventHandlerFuture: core::future::Future>, EventHandler: Fn(Event) -> EventHandlerFuture, PS: 'static + Deref + Send, + ES: 'static + Deref + Send, M: 'static - + Deref::Signer, CF, T, F, L, P>> + + Deref::Signer, CF, T, F, L, P, ES>> + Send + Sync, CM: 'static + Deref + Send + Sync, @@ -764,6 +766,7 @@ where L::Target: 'static + Logger, P::Target: 'static + Persist<::Signer>, PS::Target: 'static + Persister<'a, CM, L, S>, + ES::Target: 'static + EntropySource, CM::Target: AChannelManager + Send + Sync, OM::Target: AOnionMessenger + Send + Sync, PM::Target: APeerManager + Send + Sync, @@ -911,8 +914,11 @@ impl BackgroundProcessor { P: 'static + Deref + Send + Sync, EH: 'static + EventHandler + Send, PS: 'static + Deref + Send, + ES: 'static + Deref + Send, M: 'static - + Deref::Signer, CF, T, F, L, P>> + + Deref< + Target = ChainMonitor<::Signer, CF, T, F, L, P, ES>, + > + Send + Sync, CM: 'static + Deref + Send + Sync, @@ -935,6 +941,7 @@ impl BackgroundProcessor { L::Target: 'static + Logger, P::Target: 'static + Persist<::Signer>, PS::Target: 'static + Persister<'a, CM, L, S>, + ES::Target: 'static + EntropySource, CM::Target: AChannelManager + Send + Sync, OM::Target: AOnionMessenger + Send + Sync, PM::Target: APeerManager + Send + Sync, @@ -1085,7 +1092,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; @@ -1160,6 +1167,7 @@ mod tests { Arc, Arc, Arc, + Arc, >; type PGS = Arc< @@ -1208,6 +1216,7 @@ mod tests { Arc, IgnoringMessageHandler, Arc, + IgnoringMessageHandler, >, >, chain_monitor: Arc, @@ -1568,6 +1577,8 @@ mod tests { logger.clone(), fee_estimator.clone(), kv_store.clone(), + keys_manager.clone(), + keys_manager.get_peer_storage_key(), )); let best_block = BestBlock::from_network(network); let params = ChainParameters { network, best_block }; @@ -1621,6 +1632,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 eec9a71d632..2d3fc02fde8 100644 --- a/lightning-liquidity/src/manager.rs +++ b/lightning-liquidity/src/manager.rs @@ -304,7 +304,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..363c0ff9e99 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; @@ -101,6 +101,7 @@ type ChainMonitor = chainmonitor::ChainMonitor< Arc, Arc, Arc, + Arc, >; type PGS = Arc< @@ -130,6 +131,7 @@ pub(crate) struct Node { >, >, Arc, + Arc, >, >, pub(crate) liquidity_manager: @@ -430,6 +432,8 @@ pub(crate) fn create_liquidity_node( logger.clone(), fee_estimator.clone(), kv_store.clone(), + keys_manager.clone(), + keys_manager.get_peer_storage_key(), )); let best_block = BestBlock::from_network(network); let chain_params = ChainParameters { network, best_block }; @@ -465,6 +469,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..096910d5b01 100644 --- a/lightning/src/chain/chainmonitor.rs +++ b/lightning/src/chain/chainmonitor.rs @@ -32,16 +32,19 @@ 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::sign::{EntropySource, PeerStorageKey}; use crate::events::{self, Event, EventHandler, ReplayEvent}; use crate::util::logger::{Logger, WithContext}; 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; @@ -231,12 +234,13 @@ impl Deref for LockedChannelMonitor<'_, Chann /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager /// [module-level documentation]: crate::chain::chainmonitor /// [`rebroadcast_pending_claims`]: Self::rebroadcast_pending_claims -pub struct ChainMonitor +pub struct ChainMonitor where C::Target: chain::Filter, - T::Target: BroadcasterInterface, - F::Target: FeeEstimator, - L::Target: Logger, - P::Target: Persist, + T::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, + P::Target: Persist, + ES::Target: EntropySource, { monitors: RwLock>>, chain_source: Option, @@ -244,6 +248,7 @@ pub struct ChainMonitor, PublicKey)>>, @@ -253,14 +258,18 @@ pub struct ChainMonitor>, + + our_peerstorage_encryption_key: PeerStorageKey, } -impl ChainMonitor +impl ChainMonitor where C::Target: chain::Filter, - T::Target: BroadcasterInterface, - F::Target: FeeEstimator, - L::Target: Logger, - P::Target: Persist, + T::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, + P::Target: Persist, + ES::Target: EntropySource, { /// Dispatches to per-channel monitors, which are responsible for updating their on-chain view /// of a channel and reacting accordingly based on transactions in the given chain data. See @@ -386,7 +395,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, entropy_source: ES, our_peerstorage_encryption_key: PeerStorageKey) -> Self { Self { monitors: RwLock::new(new_hash_map()), chain_source, @@ -394,9 +411,12 @@ where C::Target: chain::Filter, logger, fee_estimator: feeest, persister, + entropy_source, 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,16 +685,67 @@ 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 all_counterparty_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 random_bytes = self.entropy_source.get_secure_random_bytes(); + let peer_storage_key = self.our_peerstorage_encryption_key; + let serialised_channels = Vec::new(); + let our_peer_storage = OurPeerStorage::create_from_data(peer_storage_key, serialised_channels, random_bytes); + 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.into_vec() } }) + } } -impl -chain::Listen for ChainMonitor +impl BaseMessageHandler for ChainMonitor +where C::Target: chain::Filter, + T::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, + P::Target: Persist, + ES::Target: EntropySource, +{ + 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 +chain::Listen for ChainMonitor where C::Target: chain::Filter, T::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, P::Target: Persist, + ES::Target: EntropySource, { fn filtered_block_connected(&self, header: &Header, txdata: &TransactionData, height: u32) { log_debug!(self.logger, "New best block {} at height {} provided via block_connected", header.block_hash(), height); @@ -682,6 +753,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.all_counterparty_node_ids() { + self.send_peer_storage(node_id); + } + // Assume we may have some new events and wake the event processor self.event_notifier.notify(); } @@ -696,14 +773,15 @@ where } } -impl -chain::Confirm for ChainMonitor +impl +chain::Confirm for ChainMonitor where C::Target: chain::Filter, T::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, P::Target: Persist, + ES::Target: EntropySource, { fn transactions_confirmed(&self, header: &Header, txdata: &TransactionData, height: u32) { log_debug!(self.logger, "{} provided transactions confirmed at height {} in block {}", txdata.len(), height, header.block_hash()); @@ -733,6 +811,12 @@ where header, height, &*self.broadcaster, &*self.fee_estimator, &self.logger ) }); + + // Send peer storage everytime a new block arrives. + for node_id in self.all_counterparty_node_ids() { + self.send_peer_storage(node_id); + } + // Assume we may have some new events and wake the event processor self.event_notifier.notify(); } @@ -750,13 +834,14 @@ where } } -impl -chain::Watch for ChainMonitor +impl +chain::Watch for ChainMonitor where C::Target: chain::Filter, - T::Target: BroadcasterInterface, - F::Target: FeeEstimator, - L::Target: Logger, - P::Target: Persist, + T::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, + P::Target: Persist, + ES::Target: EntropySource, { fn watch_channel(&self, channel_id: ChannelId, monitor: ChannelMonitor) -> Result { let logger = WithChannelMonitor::from(&self.logger, &monitor, None); @@ -889,12 +974,13 @@ where C::Target: chain::Filter, } } -impl events::EventsProvider for ChainMonitor +impl events::EventsProvider for ChainMonitor where C::Target: chain::Filter, T::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, P::Target: Persist, + ES::Target: EntropySource, { /// Processes [`SpendableOutputs`] events produced from each [`ChannelMonitor`] upon maturity. /// diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index c8f13a12bae..cba99fc495d 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -33,7 +33,7 @@ use crate::offers::invoice::UnsignedBolt12Invoice; use crate::offers::nonce::Nonce; use crate::prelude::*; use crate::routing::router::{BlindedTail, Path, Payee, PaymentParameters, RouteHop, RouteParameters, TrampolineHop}; -use crate::sign::{NodeSigner, Recipient}; +use crate::sign::{NodeSigner, PeerStorageKey, Recipient}; use crate::util::config::UserConfig; use crate::util::ser::{WithoutLength, Writeable}; use crate::util::test_utils; @@ -1610,6 +1610,7 @@ fn route_blinding_spec_test_vector() { fn sign_invoice( &self, _invoice: &RawBolt11Invoice, _recipient: Recipient, ) -> Result { unreachable!() } + fn get_peer_storage_key(&self) -> PeerStorageKey { 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) -> PeerStorageKey { 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 53fc3e6dfde..6cb487530f3 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))] @@ -8322,15 +8323,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; - Err(MsgHandleErrInternal::from_chan_no_close(ChannelError::Warn( - "Invalid peer_storage_retrieval message received.".into(), - ), ChannelId([0; 32]))) + 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).unwrap(); + + 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]))); + } + } + + Ok(()) } fn internal_peer_storage(&self, counterparty_node_id: PublicKey, msg: msgs::PeerStorage) -> Result<(), MsgHandleErrInternal> { @@ -15150,9 +15176,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()); @@ -15164,9 +15207,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); @@ -15179,30 +15237,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] @@ -16299,7 +16336,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::*; @@ -16362,9 +16399,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), @@ -16372,9 +16409,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 53940b9296d..7d56ed34bb5 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..e78d26d4996 --- /dev/null +++ b/lightning/src/ln/our_peer_storage.rs @@ -0,0 +1,139 @@ +// 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 bitcoin::hashes::sha256::Hash as Sha256; +use bitcoin::hashes::{Hash, HashEngine, Hmac, HmacEngine}; + +use crate::sign::PeerStorageKey; + +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. +/// +/// This structure is designed to serialize channel data for backup and supports encryption +/// and decryption using `ChaCha20Poly1305RFC` to ensure data integrity and security during exchange or storage. +/// +/// # Key Methods +/// - [`OurPeerStorage::create_from_data`]: Returns an encrypted [`OurPeerStorage`] instance created from the provided data. +/// - [`OurPeerStorage::decrypt_our_peer_storage`]: Decrypts the [`OurPeerStorage::encrypted_data`] using the key and returns decrypted data. +/// +/// ## Example +/// ``` +/// use lightning::ln::our_peer_storage::OurPeerStorage; +/// use lightning::sign::PeerStorageKey; +/// let key = PeerStorageKey{inner: [0u8; 32]}; +/// let our_peer_storage = OurPeerStorage::create_from_data(key.clone(), vec![1, 2, 3], [0u8; 32]); +/// 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) -> Result { + // Length of tag + Length of random_bytes + const MIN_CYPHERTEXT_LEN: usize = 16 + 32; + + if encrypted_data.len() < MIN_CYPHERTEXT_LEN { + Err(()) + } else { + Ok(Self { encrypted_data }) + } + } + + /// Get encrypted data stored inside [`OurPeerStorage`]. + pub fn into_vec(self) -> Vec { + self.encrypted_data + } + + /// Nonce for encryption and decryption: Hmac(Sha256(key) + random_bytes). + fn derive_nonce(key: &PeerStorageKey, random_bytes: &[u8]) -> [u8; 12] { + let key_hash = Sha256::const_hash(&key.inner); + + let mut hmac = HmacEngine::::new(key_hash.as_byte_array()); + hmac.input(&random_bytes); + let mut nonce = [0u8; 12]; + // First 4 bytes of the nonce should be 0. + nonce[4..].copy_from_slice(&Hmac::from_engine(hmac).to_byte_array()[0..8]); + + nonce + } + + /// Creates a serialised representation of [`OurPeerStorage`] from the given `ser_channels` data. + /// + /// This function takes a `key` (for encryption), `ser_channels` data + /// (serialised channel information) and random_bytes (to derive nonce for encryption) 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: PeerStorageKey, mut ser_channels: Vec, random_bytes: [u8; 32], + ) -> OurPeerStorage { + let plaintext_len = ser_channels.len(); + let nonce = OurPeerStorage::derive_nonce(&key, &random_bytes); + + let mut chacha = ChaCha20Poly1305RFC::new(&key.inner, &nonce, b""); + let mut tag = [0; 16]; + chacha.encrypt_full_message_in_place(&mut ser_channels[0..plaintext_len], &mut tag); + + ser_channels.extend_from_slice(&tag); + + // Append `random_bytes` in front of the encrypted_blob. + ser_channels.splice(0..0, random_bytes); + Self { encrypted_data: ser_channels } + } + + /// 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(mut self, key: PeerStorageKey) -> Result, ()> { + let cyphertext_len = self.encrypted_data.len(); + + // Ciphertext is of the form: random_bytes(32 bytes) + encrypted_data + tag(16 bytes). + let (data_mut, tag) = self.encrypted_data.split_at_mut(cyphertext_len - 16); + let (random_bytes, encrypted_data) = data_mut.split_at_mut(32); + + let nonce = OurPeerStorage::derive_nonce(&key, random_bytes); + + let mut chacha = ChaCha20Poly1305RFC::new(&key.inner, &nonce, b""); + + if chacha.check_decrypt_in_place(encrypted_data, tag).is_err() { + return Err(()); + } + + self.encrypted_data.truncate(cyphertext_len - 16); + self.encrypted_data.drain(0..32); + + Ok(self.encrypted_data) + } +} + +#[cfg(test)] +mod tests { + use crate::ln::our_peer_storage::OurPeerStorage; + use crate::sign::PeerStorageKey; + + #[test] + fn test_peer_storage_encryption_decryption() { + let key = PeerStorageKey { inner: [0u8; 32] }; + + let our_peer_storage = + OurPeerStorage::create_from_data(key.clone(), vec![42u8; 32], [200; 32]); + let decrypted_data = our_peer_storage.decrypt_our_peer_storage(key).unwrap(); + assert_eq!(decrypted_data, vec![42u8; 32]); + } +} diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index b4383210802..8770e972410 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, 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, &'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); @@ -2496,6 +2518,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); @@ -3008,7 +3035,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}; @@ -3039,7 +3066,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}; @@ -3441,12 +3468,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 8e2d8697a47..f9c4c31b06c 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -794,6 +794,13 @@ pub trait ChannelSigner { fn channel_keys_id(&self) -> [u8; 32]; } +/// Represents Secret Key used for encrypting Peer Storage. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct PeerStorageKey { + /// Represents the key used to encrypt and decrypt Peer Storage. + pub inner: [u8; 32], +} + /// Specifies the recipient of an invoice. /// /// This indicates to [`NodeSigner::sign_invoice`] what node secret key should be used to sign @@ -832,6 +839,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) -> PeerStorageKey; + /// Get node id based on the provided [`Recipient`]. /// /// This method must return the same value each time it is called with a given [`Recipient`] @@ -1771,6 +1787,7 @@ pub struct KeysManager { shutdown_pubkey: PublicKey, channel_master_key: Xpriv, channel_child_index: AtomicUsize, + peer_storage_key: PeerStorageKey, #[cfg(test)] pub(crate) entropy_source: RandomBytes, @@ -1801,18 +1818,30 @@ impl KeysManager { /// /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor pub fn new(seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32) -> Self { + // Constants for key derivation path indices used in this function. + const NODE_SECRET_INDEX: u32 = 0; + const DESTINATION_SCRIPT_INDEX: u32 = 1; + const SHUTDOWN_PUBKEY_INDEX: u32 = 2; + const CHANNEL_MASTER_KEY_INDEX: u32 = 3; + const INBOUND_PAYMENT_KEY_INDEX: u32 = 5; + const PEER_STORAGE_KEY_INDEX: u32 = 6; + let secp_ctx = Secp256k1::new(); // Note that when we aren't serializing the key, network doesn't matter match Xpriv::new_master(Network::Testnet, seed) { Ok(master_key) => { let node_secret = master_key - .derive_priv(&secp_ctx, &ChildNumber::from_hardened_idx(0).unwrap()) + .derive_priv( + &secp_ctx, + &ChildNumber::from_hardened_idx(NODE_SECRET_INDEX).unwrap(), + ) .expect("Your RNG is busted") .private_key; let node_id = PublicKey::from_secret_key(&secp_ctx, &node_secret); - let destination_script = match master_key - .derive_priv(&secp_ctx, &ChildNumber::from_hardened_idx(1).unwrap()) - { + let destination_script = match master_key.derive_priv( + &secp_ctx, + &ChildNumber::from_hardened_idx(DESTINATION_SCRIPT_INDEX).unwrap(), + ) { Ok(destination_key) => { let wpubkey_hash = WPubkeyHash::hash( &Xpub::from_priv(&secp_ctx, &destination_key).to_pub().to_bytes(), @@ -1824,21 +1853,35 @@ impl KeysManager { }, Err(_) => panic!("Your RNG is busted"), }; - let shutdown_pubkey = match master_key - .derive_priv(&secp_ctx, &ChildNumber::from_hardened_idx(2).unwrap()) - { + let shutdown_pubkey = match master_key.derive_priv( + &secp_ctx, + &ChildNumber::from_hardened_idx(SHUTDOWN_PUBKEY_INDEX).unwrap(), + ) { Ok(shutdown_key) => Xpub::from_priv(&secp_ctx, &shutdown_key).public_key, Err(_) => panic!("Your RNG is busted"), }; let channel_master_key = master_key - .derive_priv(&secp_ctx, &ChildNumber::from_hardened_idx(3).unwrap()) + .derive_priv( + &secp_ctx, + &ChildNumber::from_hardened_idx(CHANNEL_MASTER_KEY_INDEX).unwrap(), + ) .expect("Your RNG is busted"); let inbound_payment_key: SecretKey = master_key - .derive_priv(&secp_ctx, &ChildNumber::from_hardened_idx(5).unwrap()) + .derive_priv( + &secp_ctx, + &ChildNumber::from_hardened_idx(INBOUND_PAYMENT_KEY_INDEX).unwrap(), + ) .expect("Your RNG is busted") .private_key; let mut inbound_pmt_key_bytes = [0; 32]; inbound_pmt_key_bytes.copy_from_slice(&inbound_payment_key[..]); + let peer_storage_key = master_key + .derive_priv( + &secp_ctx, + &ChildNumber::from_hardened_idx(PEER_STORAGE_KEY_INDEX).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()); @@ -1854,6 +1897,8 @@ impl KeysManager { node_id, inbound_payment_key: ExpandedKey::new(inbound_pmt_key_bytes), + peer_storage_key: PeerStorageKey { inner: peer_storage_key.secret_bytes() }, + destination_script, shutdown_pubkey, @@ -2079,6 +2124,10 @@ impl NodeSigner for KeysManager { self.inbound_payment_key.clone() } + fn get_peer_storage_key(&self) -> PeerStorageKey { + self.peer_storage_key.clone() + } + fn sign_invoice( &self, invoice: &RawBolt11Invoice, recipient: Recipient, ) -> Result { @@ -2240,6 +2289,10 @@ impl NodeSigner for PhantomKeysManager { self.inbound_payment_key.clone() } + fn get_peer_storage_key(&self) -> PeerStorageKey { + self.inner.peer_storage_key.clone() + } + fn sign_invoice( &self, invoice: &RawBolt11Invoice, recipient: Recipient, ) -> Result { diff --git a/lightning/src/util/anchor_channel_reserves.rs b/lightning/src/util/anchor_channel_reserves.rs index 968a60ada0b..ebae770fb8a 100644 --- a/lightning/src/util/anchor_channel_reserves.rs +++ b/lightning/src/util/anchor_channel_reserves.rs @@ -29,6 +29,7 @@ use crate::ln::chan_utils::max_htlcs; use crate::ln::channelmanager::AChannelManager; use crate::prelude::new_hash_set; use crate::sign::ecdsa::EcdsaChannelSigner; +use crate::sign::EntropySource; use crate::types::features::ChannelTypeFeatures; use crate::util::logger::Logger; use bitcoin::constants::WITNESS_SCALE_FACTOR; @@ -276,6 +277,7 @@ pub fn can_support_additional_anchor_channel< EstimatorRef: Deref, LoggerRef: Deref, PersistRef: Deref, + EntropySourceRef: Deref, ChainMonitorRef: Deref< Target = ChainMonitor< ChannelSigner, @@ -284,6 +286,7 @@ pub fn can_support_additional_anchor_channel< EstimatorRef, LoggerRef, PersistRef, + EntropySourceRef, >, >, >( @@ -297,6 +300,7 @@ where EstimatorRef::Target: FeeEstimator, LoggerRef::Target: Logger, PersistRef::Target: Persist, + EntropySourceRef::Target: EntropySource, { let mut anchor_channels = new_hash_set(); // Calculate the number of in-progress anchor channels by inspecting ChannelMonitors with balance. diff --git a/lightning/src/util/dyn_signer.rs b/lightning/src/util/dyn_signer.rs index 939e40cf7c4..7e9844e2ac0 100644 --- a/lightning/src/util/dyn_signer.rs +++ b/lightning/src/util/dyn_signer.rs @@ -17,7 +17,9 @@ use crate::sign::taproot::TaprootChannelSigner; use crate::sign::ChannelSigner; use crate::sign::InMemorySigner; use crate::sign::{EntropySource, HTLCDescriptor, OutputSpender, PhantomKeysManager}; -use crate::sign::{NodeSigner, Recipient, SignerProvider, SpendableOutputDescriptor}; +use crate::sign::{ + NodeSigner, PeerStorageKey, Recipient, SignerProvider, SpendableOutputDescriptor, +}; use bitcoin; use bitcoin::absolute::LockTime; use bitcoin::secp256k1::All; @@ -214,7 +216,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(,) -> PeerStorageKey ); delegate!(DynKeysInterface, SignerProvider, @@ -278,7 +281,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(,) -> PeerStorageKey ); impl SignerProvider for DynPhantomKeysInterface { diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 2d7d6fb5876..81a6f8d1ad7 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -46,7 +46,7 @@ use crate::routing::router::{ use crate::routing::scoring::{ChannelUsage, ScoreLookUp, ScoreUpdate}; use crate::routing::utxo::{UtxoLookup, UtxoLookupError, UtxoResult}; use crate::sign; -use crate::sign::ChannelSigner; +use crate::sign::{ChannelSigner, PeerStorageKey}; use crate::sync::RwLock; use crate::types::features::{ChannelFeatures, InitFeatures, NodeFeatures}; use crate::util::config::UserConfig; @@ -407,6 +407,7 @@ pub struct TestChainMonitor<'a> { &'a TestFeeEstimator, &'a TestLogger, &'a dyn SyncPersist, + &'a TestKeysInterface, >, pub keys_manager: &'a TestKeysInterface, /// If this is set to Some(), the next update_channel call (not watch_channel) must be a @@ -435,6 +436,8 @@ impl<'a> TestChainMonitor<'a> { logger, fee_estimator, persister, + keys_manager, + keys_manager.get_peer_storage_key(), ), keys_manager, expect_channel_force_closed: Mutex::new(None), @@ -1482,6 +1485,10 @@ impl NodeSigner for TestNodeSigner { unreachable!() } + fn get_peer_storage_key(&self) -> PeerStorageKey { + unreachable!() + } + fn get_node_id(&self, recipient: Recipient) -> Result { let node_secret = match recipient { Recipient::Node => Ok(&self.node_secret), @@ -1561,6 +1568,10 @@ impl NodeSigner for TestKeysInterface { self.backing.sign_invoice(invoice, recipient) } + fn get_peer_storage_key(&self) -> PeerStorageKey { + self.backing.get_peer_storage_key() + } + fn sign_bolt12_invoice( &self, invoice: &UnsignedBolt12Invoice, ) -> Result {