From a0bf86c359a6a38fafbdec239c267861cdc84213 Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Sat, 18 Jan 2025 01:22:13 +0530 Subject: [PATCH 01/19] Add method to derive Peer Storage encryption key Add get_peer_storage_key method to derive a 32-byte encryption key for securing Peer Storage. This method utilizes HKDF with the node's secret key as input and a fixed info string to generate the encryption key. - Add 'get_peer_storage_key' to NodeSigner. - Implement 'get_peer_storage_key' for KeysManager & PhantomKeysManager. --- fuzz/src/chanmon_consistency.rs | 8 +++++- fuzz/src/full_stack.rs | 20 ++++++++++----- fuzz/src/onion_message.rs | 5 ++++ lightning/src/ln/blinded_payment_tests.rs | 4 ++- lightning/src/ln/channelmanager.rs | 4 +-- lightning/src/sign/mod.rs | 31 +++++++++++++++++++++++ lightning/src/util/dyn_signer.rs | 10 +++++--- lightning/src/util/test_utils.rs | 10 +++++++- 8 files changed, 77 insertions(+), 15 deletions(-) diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 1869dfb0d1c..9139619bdbc 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::*; @@ -337,6 +339,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..16356765a85 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; @@ -420,6 +420,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,6 +612,14 @@ 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(), @@ -616,12 +628,6 @@ pub fn do_test(mut data: &[u8], logger: &Arc) { Arc::new(TestPersister { update_ret: Mutex::new(ChannelMonitorUpdateStatus::Completed) }), )); - 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) }; 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/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..ee969dd8db6 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -16362,9 +16362,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 +16372,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/sign/mod.rs b/lightning/src/sign/mod.rs index 8e2d8697a47..f1af198d47b 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, 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, @@ -1839,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()); @@ -1854,6 +1875,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 +2102,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 +2267,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/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..ef38391b69d 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; @@ -1482,6 +1482,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 +1565,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 { From 66090c276c8eb20ca2dd466f9fe55807563fd502 Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Wed, 26 Feb 2025 08:48:50 +0530 Subject: [PATCH 02/19] Add OurPeerStorage for serialized Peer Storage backups Introduce the OurPeerStorage struct to manage serialized channel data for peer storage backups. This struct facilitates the distribution of peer storage to channel partners and includes versioning and timestamping for comparison between retrieved peer storage instances. - Add the OurPeerStorage struct with fields for version, timestamp, and serialized channel data (ser_channels). - Implement methods to encrypt and decrypt peer storage securely. - Add functionality to update channel data within OurPeerStorage. --- lightning/src/ln/mod.rs | 1 + lightning/src/ln/our_peer_storage.rs | 111 +++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 lightning/src/ln/our_peer_storage.rs 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..634e602255a --- /dev/null +++ b/lightning/src/ln/our_peer_storage.rs @@ -0,0 +1,111 @@ +// 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::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. 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 +/// ``` +/// 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]); +/// 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: PeerStorageKey, mut ser_channels: Vec) -> OurPeerStorage { + let n = 0u64; + + let plaintext_len = ser_channels.len(); + + let mut nonce = [0; 12]; + nonce[4..].copy_from_slice(&n.to_le_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); + + 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, ()> { + const MIN_CYPHERTEXT_LEN: usize = 16; + let cyphertext_len = self.encrypted_data.len(); + + // Ensure the cyphertext is at least as large as the MIN_CYPHERTEXT_LEN. + if cyphertext_len < MIN_CYPHERTEXT_LEN { + return Err(()); + } + + // Split the cyphertext into the encrypted data and the authentication tag. + let (encrypted_data, tag) = self.encrypted_data.split_at_mut(cyphertext_len - 16); + + let n = 0u64; + let mut nonce = [0; 12]; + nonce[4..].copy_from_slice(&n.to_le_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); + + Ok(self.encrypted_data) + } +} From fa7e350929c545b419a0bbd38137ccd675fb150e Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Sat, 18 Jan 2025 18:17:19 +0530 Subject: [PATCH 03/19] Enable ChainMonitor to distribute PeerStorage To enable ChainMonitor sending peer storage to channel partners whenever a new block is added, We implement BaseMessageHandler for ChainMonitor. This allows the `ChainMonitor` to handle the peer storage distribution. Key changes: - Add BaseMessageHandler into the MessageHandler. - Implement BaseMessageHandler for ChainMonitor. - Process BaseMessageHandler events inside process_events(). --- fuzz/src/chanmon_consistency.rs | 1 + fuzz/src/full_stack.rs | 3 + lightning-background-processor/src/lib.rs | 7 ++- lightning-liquidity/src/manager.rs | 2 +- lightning-liquidity/tests/common/mod.rs | 5 +- lightning-net-tokio/src/lib.rs | 3 + lightning/src/chain/chainmonitor.rs | 57 ++++++++++++++++- lightning/src/ln/peer_handler.rs | 75 ++++++++++++++++------- lightning/src/util/test_utils.rs | 1 + 9 files changed, 125 insertions(+), 29 deletions(-) diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 9139619bdbc..4da2e99fb1a 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -206,6 +206,7 @@ impl TestChainMonitor { logger.clone(), feeest, Arc::clone(&persister), + keys.get_peer_storage_key(), )), logger, keys, diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index 16356765a85..7ec06a633c6 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> { @@ -626,6 +627,7 @@ pub fn do_test(mut data: &[u8], logger: &Arc) { Arc::clone(&logger), fee_est.clone(), Arc::new(TestPersister { update_ret: Mutex::new(ChannelMonitorUpdateStatus::Completed) }), + keys_manager.get_peer_storage_key(), )); let network = Network::Bitcoin; @@ -659,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/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 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..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..79fb5334f7c 100644 --- a/lightning/src/chain/chainmonitor.rs +++ b/lightning/src/chain/chainmonitor.rs @@ -32,16 +32,18 @@ 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::sign::ecdsa::EcdsaChannelSigner; +use crate::sign::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; @@ -253,6 +255,7 @@ pub struct ChainMonitor>, } impl ChainMonitor @@ -386,7 +389,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: PeerStorageKey) -> Self { Self { monitors: RwLock::new(new_hash_map()), chain_source, @@ -397,6 +408,7 @@ 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()), } } @@ -665,6 +677,47 @@ 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` and update [`OurPeerStorage::block_height`] accordingly. + } +} + +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 diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index b4383210802..fa4b1463ce1 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); @@ -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/util/test_utils.rs b/lightning/src/util/test_utils.rs index ef38391b69d..d304687aa63 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -435,6 +435,7 @@ impl<'a> TestChainMonitor<'a> { logger, fee_estimator, persister, + keys_manager.get_peer_storage_key(), ), keys_manager, expect_channel_force_closed: Mutex::new(None), From d5a996ab001d1475251c9adaaccaff284b86c7c4 Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Sun, 19 Jan 2025 16:10:04 +0530 Subject: [PATCH 04/19] Distribute PeerStorage from ChainMonitor Everytime a new block is added we send PeerStorage to all of our channel partner. - Add our_peer_storage and our_peerstorage_encryption_key to ChainMonitor - Write send_peer_storage() and send it to all channel partners whenever a new block is added --- lightning/src/chain/chainmonitor.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lightning/src/chain/chainmonitor.rs b/lightning/src/chain/chainmonitor.rs index 79fb5334f7c..ec570d7a194 100644 --- a/lightning/src/chain/chainmonitor.rs +++ b/lightning/src/chain/chainmonitor.rs @@ -33,6 +33,7 @@ use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, Balance 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::PeerStorageKey; use crate::events::{self, Event, EventHandler, ReplayEvent}; @@ -256,6 +257,8 @@ pub struct ChainMonitor>, + + our_peerstorage_encryption_key: PeerStorageKey, } impl ChainMonitor @@ -409,6 +412,7 @@ where C::Target: chain::Filter, highest_chain_height: AtomicUsize::new(0), event_notifier: Notifier::new(), pending_send_only_events: Mutex::new(Vec::new()), + our_peerstorage_encryption_key } } @@ -691,7 +695,12 @@ where C::Target: chain::Filter, } fn send_peer_storage(&self, their_node_id: PublicKey) { - // TODO: Serialize `ChannelMonitor`s inside `our_peer_storage` and update [`OurPeerStorage::block_height`] accordingly. + // TODO: Serialize `ChannelMonitor`s inside `our_peer_storage`. + + let our_peer_storage = OurPeerStorage::create_from_data(self.our_peerstorage_encryption_key.clone(), 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() } }) } } @@ -735,6 +744,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(); } @@ -786,6 +801,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(); } From 99b0572d85765cd55073c8a0c17284d6bd58e640 Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Sun, 19 Jan 2025 21:49:04 +0530 Subject: [PATCH 05/19] Handle PeerStorageRetrieval in ChannelManager Ensure ChannelManager properly handles peer_storage_retrieval. - Write internal_peer_storage_retreival to verify if we recv correct peer storage. - Send error if we get invalid peer_storage data. --- lightning/src/ln/channelmanager.rs | 40 ++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index ee969dd8db6..4885fd91697 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); + + 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> { @@ -16299,7 +16325,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::*; From 306943e4d506217dcb333a880c87a7b8f6e74404 Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Sat, 8 Mar 2025 02:50:12 +0530 Subject: [PATCH 06/19] test: update test_peer_storage to validate latest changes Ensure that we correctly handle the sendpeerstorage message event from chainmonitor and process it through channelmonitor. Key Changes: - Retrieve sendpeerstorage message event from chainmonitor for both nodes. - Handle peer storage messages exchanged between nodes and verify correct decryption. --- lightning/src/ln/channelmanager.rs | 65 +++++++++++++++++------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 4885fd91697..82e730b5026 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -15176,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()); @@ -15190,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); @@ -15205,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] From 09c7748d8627a14c4df544d180ef2841cdaad10a Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Thu, 13 Mar 2025 23:10:52 +0530 Subject: [PATCH 07/19] Introduce constants for derivation path indices This commit replaces magic numbers with descriptive constant names for the indices used in key derivation paths within the `new` function. - Added constants: - `NODE_SECRET_INDEX` - `DESTINATION_SCRIPT_INDEX` - `SHUTDOWN_PUBKEY_INDEX` - `CHANNEL_MASTER_KEY_INDEX` - `INBOUND_PAYMENT_KEY_INDEX` - `PEER_STORAGE_KEY_INDEX` --- lightning/src/sign/mod.rs | 44 +++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs index f1af198d47b..4cf6cd91e1a 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -1818,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(), @@ -1841,23 +1853,33 @@ 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: SecretKey = master_key - .derive_priv(&secp_ctx, &ChildNumber::from_hardened_idx(6).unwrap()) + 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; From 680731cc96abd47c1b6d24a957a56631a161099b Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Mon, 7 Apr 2025 23:07:44 +0530 Subject: [PATCH 08/19] fixup: Enable ChainMonitor to distribute PeerStorage --- lightning-background-processor/src/lib.rs | 9 ++++- lightning-liquidity/tests/common/mod.rs | 2 + lightning/src/chain/chainmonitor.rs | 35 ++++++++++------ lightning/src/ln/our_peer_storage.rs | 40 ++++++++++++++----- lightning/src/ln/peer_handler.rs | 4 +- lightning/src/util/anchor_channel_reserves.rs | 4 ++ lightning/src/util/test_utils.rs | 2 + 7 files changed, 70 insertions(+), 26 deletions(-) diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs index 845f79704e1..d7a522d459f 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")] @@ -911,8 +912,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 +939,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, @@ -1160,6 +1165,7 @@ mod tests { Arc, Arc, Arc, + Arc, >; type PGS = Arc< @@ -1569,6 +1575,7 @@ 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); diff --git a/lightning-liquidity/tests/common/mod.rs b/lightning-liquidity/tests/common/mod.rs index 17a0d10dc9f..363c0ff9e99 100644 --- a/lightning-liquidity/tests/common/mod.rs +++ b/lightning-liquidity/tests/common/mod.rs @@ -101,6 +101,7 @@ type ChainMonitor = chainmonitor::ChainMonitor< Arc, Arc, Arc, + Arc, >; type PGS = Arc< @@ -431,6 +432,7 @@ 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); diff --git a/lightning/src/chain/chainmonitor.rs b/lightning/src/chain/chainmonitor.rs index ec570d7a194..fdf07030a7f 100644 --- a/lightning/src/chain/chainmonitor.rs +++ b/lightning/src/chain/chainmonitor.rs @@ -35,7 +35,7 @@ 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::PeerStorageKey; +use crate::sign::{EntropySource, PeerStorageKey}; use crate::events::{self, Event, EventHandler, ReplayEvent}; use crate::util::logger::{Logger, WithContext}; use crate::util::errors::APIError; @@ -234,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, + ES::Target: EntropySource, { monitors: RwLock>>, chain_source: Option, @@ -247,6 +248,7 @@ pub struct ChainMonitor, PublicKey)>>, @@ -261,12 +263,13 @@ pub struct ChainMonitor ChainMonitor +impl ChainMonitor where C::Target: chain::Filter, 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 @@ -400,7 +403,7 @@ where C::Target: chain::Filter, /// **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: PeerStorageKey) -> Self { + 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, @@ -408,6 +411,7 @@ 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(), @@ -697,19 +701,20 @@ where C::Target: chain::Filter, 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.clone(), Vec::new()); + let our_peer_storage = OurPeerStorage::create_from_data(self.our_peerstorage_encryption_key.clone(), Vec::new(), self.entropy_source.get_secure_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.encrypted_data() } }) } } -impl BaseMessageHandler 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(); @@ -729,14 +734,15 @@ where C::Target: chain::Filter, fn peer_connected(&self, _their_node_id: PublicKey, _msg: &Init, _inbound: bool) -> Result<(), ()> { Ok(()) } } -impl -chain::Listen for ChainMonitor +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); @@ -764,14 +770,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()); @@ -824,13 +831,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, + ES::Target: EntropySource, { fn watch_channel(&self, channel_id: ChannelId, monitor: ChannelMonitor) -> Result { let logger = WithChannelMonitor::from(&self.logger, &monitor, None); @@ -963,12 +971,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/our_peer_storage.rs b/lightning/src/ln/our_peer_storage.rs index 634e602255a..43de78e9ec4 100644 --- a/lightning/src/ln/our_peer_storage.rs +++ b/lightning/src/ln/our_peer_storage.rs @@ -11,6 +11,9 @@ //! 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}; + use crate::sign::PeerStorageKey; use crate::crypto::chacha20poly1305rfc::ChaCha20Poly1305RFC; @@ -63,13 +66,20 @@ impl OurPeerStorage { /// (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: PeerStorageKey, mut ser_channels: Vec) -> OurPeerStorage { - let n = 0u64; + pub fn create_from_data( + key: PeerStorageKey, mut ser_channels: Vec, random_bytes: [u8; 32], + ) -> OurPeerStorage { + let key_hash = Sha256::const_hash(&key.inner); let plaintext_len = ser_channels.len(); - let mut nonce = [0; 12]; - nonce[4..].copy_from_slice(&n.to_le_bytes()[..]); + // Compute Sha256(Sha256(key) + random_bytes). + let mut sha = Sha256::engine(); + sha.input(&key_hash.to_byte_array()); + sha.input(&random_bytes); + + let mut nonce = [0u8; 12]; + nonce[4..].copy_from_slice(&Sha256::from_engine(sha).to_byte_array()[0..8]); let mut chacha = ChaCha20Poly1305RFC::new(&key.inner, &nonce, b""); let mut tag = [0; 16]; @@ -77,13 +87,18 @@ impl OurPeerStorage { 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, ()> { - const MIN_CYPHERTEXT_LEN: usize = 16; + let key_hash = Sha256::const_hash(&key.inner); + + // Length of tag + Length of random_bytes + const MIN_CYPHERTEXT_LEN: usize = 16 + 32; let cyphertext_len = self.encrypted_data.len(); // Ensure the cyphertext is at least as large as the MIN_CYPHERTEXT_LEN. @@ -91,12 +106,16 @@ impl OurPeerStorage { return Err(()); } - // Split the cyphertext into the encrypted data and the authentication tag. - let (encrypted_data, tag) = self.encrypted_data.split_at_mut(cyphertext_len - 16); + // 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 mut sha = Sha256::engine(); + sha.input(&key_hash.to_byte_array()); + sha.input(random_bytes); - let n = 0u64; - let mut nonce = [0; 12]; - nonce[4..].copy_from_slice(&n.to_le_bytes()[..]); + let mut nonce = [0u8; 12]; + nonce[4..].copy_from_slice(&Sha256::from_engine(sha).to_byte_array()[0..8]); let mut chacha = ChaCha20Poly1305RFC::new(&key.inner, &nonce, b""); @@ -105,6 +124,7 @@ impl OurPeerStorage { } self.encrypted_data.truncate(cyphertext_len - 16); + self.encrypted_data.drain(0..32); Ok(self.encrypted_data) } diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index fa4b1463ce1..8770e972410 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -721,7 +721,7 @@ pub type SimpleArcPeerManager = PeerManager< Arc, IgnoringMessageHandler, Arc, - Arc, Arc, Arc, Arc, Arc>>, + Arc, Arc, Arc, Arc, Arc, Arc>>, >; /// SimpleRefPeerManager is a type alias for a PeerManager reference, and is the reference @@ -743,7 +743,7 @@ pub type SimpleRefPeerManager< &'logger L, IgnoringMessageHandler, &'c KeysManager, - &'j ChainMonitor<&'a M, C, &'b T, &'c F, &'logger L, &'c KeysManager>, + &'j ChainMonitor<&'a M, C, &'b T, &'c F, &'logger L, &'c KeysManager, &'c KeysManager>, >; 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/test_utils.rs b/lightning/src/util/test_utils.rs index d304687aa63..81a6f8d1ad7 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -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,7 @@ impl<'a> TestChainMonitor<'a> { logger, fee_estimator, persister, + keys_manager, keys_manager.get_peer_storage_key(), ), keys_manager, From 4e4c4aea009a5d862fd17f69537d5b342a9bccb2 Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Mon, 7 Apr 2025 23:45:54 +0530 Subject: [PATCH 09/19] fixup: Add OurPeerStorage for serialized Peer Storage backups --- lightning/src/chain/chainmonitor.rs | 2 +- lightning/src/ln/our_peer_storage.rs | 25 ++++++++++--------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/lightning/src/chain/chainmonitor.rs b/lightning/src/chain/chainmonitor.rs index fdf07030a7f..fc764a09f69 100644 --- a/lightning/src/chain/chainmonitor.rs +++ b/lightning/src/chain/chainmonitor.rs @@ -704,7 +704,7 @@ where C::Target: chain::Filter, let our_peer_storage = OurPeerStorage::create_from_data(self.our_peerstorage_encryption_key.clone(), Vec::new(), self.entropy_source.get_secure_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.encrypted_data() } }) + msg: msgs::PeerStorage { data: our_peer_storage.into_vec() } }) } } diff --git a/lightning/src/ln/our_peer_storage.rs b/lightning/src/ln/our_peer_storage.rs index 43de78e9ec4..1dc44ceeaf2 100644 --- a/lightning/src/ln/our_peer_storage.rs +++ b/lightning/src/ln/our_peer_storage.rs @@ -20,27 +20,21 @@ 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`]. +/// `peer_storage` backup. /// /// 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. +/// and decryption using `ChaCha20Poly1305RFC` 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. +/// - [`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]); +/// 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]); /// ``` @@ -56,14 +50,15 @@ impl OurPeerStorage { } /// Get encrypted data stored inside [`OurPeerStorage`]. - pub fn encrypted_data(&self) -> Vec { - self.encrypted_data.clone() + pub fn into_vec(self) -> Vec { + self.encrypted_data } /// 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`. + /// 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( From c0e5008713fdbc00d540dae875bbdcdf8e5c7ca1 Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Wed, 9 Apr 2025 16:50:56 +0530 Subject: [PATCH 10/19] fixup: Enable ChainMonitor to distribute PeerStorage --- lightning/src/chain/chainmonitor.rs | 45 +++++++++++++++-------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/lightning/src/chain/chainmonitor.rs b/lightning/src/chain/chainmonitor.rs index fc764a09f69..096910d5b01 100644 --- a/lightning/src/chain/chainmonitor.rs +++ b/lightning/src/chain/chainmonitor.rs @@ -236,11 +236,11 @@ impl Deref for LockedChannelMonitor<'_, Chann /// [`rebroadcast_pending_claims`]: Self::rebroadcast_pending_claims pub struct ChainMonitor where C::Target: chain::Filter, - T::Target: BroadcasterInterface, - F::Target: FeeEstimator, - L::Target: Logger, - P::Target: Persist, - ES::Target: EntropySource, + T::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, + P::Target: Persist, + ES::Target: EntropySource, { monitors: RwLock>>, chain_source: Option, @@ -265,11 +265,11 @@ pub struct ChainMonitor ChainMonitor where C::Target: chain::Filter, - T::Target: BroadcasterInterface, - F::Target: FeeEstimator, - L::Target: Logger, - P::Target: Persist, - ES::Target: EntropySource, + 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 @@ -701,7 +701,10 @@ where C::Target: chain::Filter, 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.clone(), Vec::new(), self.entropy_source.get_secure_random_bytes()); + 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() } }) @@ -710,11 +713,11 @@ where C::Target: chain::Filter, impl BaseMessageHandler for ChainMonitor where C::Target: chain::Filter, - T::Target: BroadcasterInterface, - F::Target: FeeEstimator, - L::Target: Logger, - P::Target: Persist, - ES::Target: EntropySource, + 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(); @@ -834,11 +837,11 @@ where impl chain::Watch for ChainMonitor where C::Target: chain::Filter, - T::Target: BroadcasterInterface, - F::Target: FeeEstimator, - L::Target: Logger, - P::Target: Persist, - ES::Target: EntropySource, + 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); From 5643873344a280dd3c5fac5e40ebd35a31e40ad4 Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Wed, 9 Apr 2025 17:08:18 +0530 Subject: [PATCH 11/19] fixup: Add method to derive Peer Storage encryption key --- lightning/src/sign/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs index 4cf6cd91e1a..f9c4c31b06c 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -795,7 +795,7 @@ pub trait ChannelSigner { } /// Represents Secret Key used for encrypting Peer Storage. -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq)] pub struct PeerStorageKey { /// Represents the key used to encrypt and decrypt Peer Storage. pub inner: [u8; 32], From 351df68d9394063a93e5609f5fb69675e6eb36d3 Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Wed, 9 Apr 2025 17:10:03 +0530 Subject: [PATCH 12/19] fixup: Handle PeerStorageRetrieval in ChannelManager --- lightning/src/ln/channelmanager.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 82e730b5026..6cb487530f3 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -8338,7 +8338,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } let our_peerstorage_encryption_key = self.node_signer.get_peer_storage_key(); - let our_peer_storage = OurPeerStorage::new(msg.data); + let our_peer_storage = OurPeerStorage::new(msg.data).unwrap(); match our_peer_storage.decrypt_our_peer_storage(our_peerstorage_encryption_key) { Ok(decrypted_data) => { From 2fbb5e550631c61e37a2886659aeef48baf6cb80 Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Wed, 9 Apr 2025 17:11:21 +0530 Subject: [PATCH 13/19] fixup: Add OurPeerStorage for serialized Peer Storage backups --- lightning/src/ln/our_peer_storage.rs | 69 +++++++++++++++++----------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/lightning/src/ln/our_peer_storage.rs b/lightning/src/ln/our_peer_storage.rs index 1dc44ceeaf2..e78d26d4996 100644 --- a/lightning/src/ln/our_peer_storage.rs +++ b/lightning/src/ln/our_peer_storage.rs @@ -12,7 +12,7 @@ //! transmission. //! use bitcoin::hashes::sha256::Hash as Sha256; -use bitcoin::hashes::{Hash, HashEngine}; +use bitcoin::hashes::{Hash, HashEngine, Hmac, HmacEngine}; use crate::sign::PeerStorageKey; @@ -45,8 +45,15 @@ pub struct OurPeerStorage { impl OurPeerStorage { /// Creates a new [`OurPeerStorage`] with given encrypted_data. - pub fn new(encrypted_data: Vec) -> Self { - Self { 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`]. @@ -54,6 +61,19 @@ impl OurPeerStorage { 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 @@ -64,17 +84,8 @@ impl OurPeerStorage { pub fn create_from_data( key: PeerStorageKey, mut ser_channels: Vec, random_bytes: [u8; 32], ) -> OurPeerStorage { - let key_hash = Sha256::const_hash(&key.inner); - let plaintext_len = ser_channels.len(); - - // Compute Sha256(Sha256(key) + random_bytes). - let mut sha = Sha256::engine(); - sha.input(&key_hash.to_byte_array()); - sha.input(&random_bytes); - - let mut nonce = [0u8; 12]; - nonce[4..].copy_from_slice(&Sha256::from_engine(sha).to_byte_array()[0..8]); + let nonce = OurPeerStorage::derive_nonce(&key, &random_bytes); let mut chacha = ChaCha20Poly1305RFC::new(&key.inner, &nonce, b""); let mut tag = [0; 16]; @@ -90,27 +101,13 @@ impl OurPeerStorage { /// 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 key_hash = Sha256::const_hash(&key.inner); - - // Length of tag + Length of random_bytes - const MIN_CYPHERTEXT_LEN: usize = 16 + 32; let cyphertext_len = self.encrypted_data.len(); - // Ensure the cyphertext is at least as large as the MIN_CYPHERTEXT_LEN. - if cyphertext_len < MIN_CYPHERTEXT_LEN { - return Err(()); - } - // 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 mut sha = Sha256::engine(); - sha.input(&key_hash.to_byte_array()); - sha.input(random_bytes); - - let mut nonce = [0u8; 12]; - nonce[4..].copy_from_slice(&Sha256::from_engine(sha).to_byte_array()[0..8]); + let nonce = OurPeerStorage::derive_nonce(&key, random_bytes); let mut chacha = ChaCha20Poly1305RFC::new(&key.inner, &nonce, b""); @@ -124,3 +121,19 @@ impl OurPeerStorage { 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]); + } +} From 28aff94fc0d608b8ae3a0658e31cdf0ff53dcaba Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Wed, 9 Apr 2025 17:28:17 +0530 Subject: [PATCH 14/19] fixup: Enable ChainMonitor to distribute PeerStorage --- lightning-background-processor/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs index d7a522d459f..25304fdaeb3 100644 --- a/lightning-background-processor/src/lib.rs +++ b/lightning-background-processor/src/lib.rs @@ -737,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, @@ -765,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, From 7abc134ea31033b443e172c66d65784f304b8fdd Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Mon, 14 Apr 2025 08:42:36 +0530 Subject: [PATCH 15/19] fixup: Add OurPeerStorage for serialized Peer Storage backups --- lightning/src/ln/our_peer_storage.rs | 85 ++++++++++++++++++---------- 1 file changed, 56 insertions(+), 29 deletions(-) diff --git a/lightning/src/ln/our_peer_storage.rs b/lightning/src/ln/our_peer_storage.rs index e78d26d4996..a4a27e373bb 100644 --- a/lightning/src/ln/our_peer_storage.rs +++ b/lightning/src/ln/our_peer_storage.rs @@ -7,10 +7,11 @@ // 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. +//! `OurPeerStorage` enables storage of encrypted serialized channel data. +//! It provides encryption and decryption of data to maintain data integrity and +//! security during transmission. //! + use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::{Hash, HashEngine, Hmac, HmacEngine}; @@ -19,14 +20,15 @@ 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 +/// [`OurPeerStorage`] is used to store encrypted 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. +/// and decryption using `ChaCha20Poly1305RFC` for transmission. /// /// # Key Methods -/// - [`OurPeerStorage::create_from_data`]: Returns an encrypted [`OurPeerStorage`] instance created from the provided data. +/// - [`OurPeerStorage::new`]: Returns [`OurPeerStorage`] with the given encrypted_data. +/// - [`OurPeerStorage::create_from_data`]: Returns [`OurPeerStorage`] created from encrypting the provided data. /// - [`OurPeerStorage::decrypt_our_peer_storage`]: Decrypts the [`OurPeerStorage::encrypted_data`] using the key and returns decrypted data. /// /// ## Example @@ -45,6 +47,8 @@ pub struct OurPeerStorage { impl OurPeerStorage { /// Creates a new [`OurPeerStorage`] with given encrypted_data. + /// Returns an error if the `encrypted_data` is less than the + /// appropriate length. pub fn new(encrypted_data: Vec) -> Result { // Length of tag + Length of random_bytes const MIN_CYPHERTEXT_LEN: usize = 16 + 32; @@ -61,31 +65,18 @@ impl OurPeerStorage { 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. + /// Returns [`OurPeerStorage`] with encrypted `ser_channels`. /// /// 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`. + /// (serialised channel information) and random_bytes (to derive nonce for encryption) and returns a + /// [`OurPeerStorage`] with encrypted data inside. /// /// 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 nonce = derive_nonce(&key, &random_bytes); let mut chacha = ChaCha20Poly1305RFC::new(&key.inner, &nonce, b""); let mut tag = [0; 16]; @@ -93,7 +84,7 @@ impl OurPeerStorage { ser_channels.extend_from_slice(&tag); - // Append `random_bytes` in front of the encrypted_blob. + // Prepend `random_bytes` in front of the encrypted_blob. ser_channels.splice(0..0, random_bytes); Self { encrypted_data: ser_channels } } @@ -107,7 +98,7 @@ impl OurPeerStorage { 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 nonce = derive_nonce(&key, random_bytes); let mut chacha = ChaCha20Poly1305RFC::new(&key.inner, &nonce, b""); @@ -122,18 +113,54 @@ impl OurPeerStorage { } } +/// 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 +} + #[cfg(test)] mod tests { - use crate::ln::our_peer_storage::OurPeerStorage; + use crate::ln::our_peer_storage::{derive_nonce, OurPeerStorage}; use crate::sign::PeerStorageKey; #[test] fn test_peer_storage_encryption_decryption() { - let key = PeerStorageKey { inner: [0u8; 32] }; + let key1 = PeerStorageKey { inner: [0u8; 32] }; + let key2 = PeerStorageKey { inner: [1u8; 32] }; + let random_bytes1 = [200; 32]; + let random_bytes2 = [201; 32]; + // Happy Path 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(); + OurPeerStorage::create_from_data(key1.clone(), vec![42u8; 32], random_bytes1); + let decrypted_data = our_peer_storage.decrypt_our_peer_storage(key1.clone()).unwrap(); assert_eq!(decrypted_data, vec![42u8; 32]); + + // Changing Key + let our_peer_storage_wrong_key = + OurPeerStorage::create_from_data(key1.clone(), vec![42u8; 32], random_bytes1); + let decrypted_data_wrong_key = our_peer_storage_wrong_key.decrypt_our_peer_storage(key2); + assert!(decrypted_data_wrong_key.is_err()); + + // Nonce derivation happy path + let nonce = derive_nonce(&key1, &random_bytes1); + let nonce_happy_path = derive_nonce(&key1, &random_bytes1); + assert_eq!(nonce, nonce_happy_path); + + // Nonce derivation with different `random_bytes` & `key` + let nonce_diff_random_bytes = derive_nonce(&key1, &random_bytes2); + let nonce_diff_key = derive_nonce(&key2, &random_bytes1); + let nonce_diff_key_random_bytes = derive_nonce(&key2, &random_bytes2); + assert_ne!(nonce, nonce_diff_random_bytes); + assert_ne!(nonce, nonce_diff_key); + assert_ne!(nonce, nonce_diff_key_random_bytes); } } From 7a6306bed3165b90ff6de27b7f4dc8bb587e1e86 Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Mon, 14 Apr 2025 08:44:48 +0530 Subject: [PATCH 16/19] fixup: Enable ChainMonitor to distribute PeerStorage --- lightning-liquidity/tests/common/mod.rs | 2 +- lightning/src/chain/chainmonitor.rs | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lightning-liquidity/tests/common/mod.rs b/lightning-liquidity/tests/common/mod.rs index 363c0ff9e99..709ace7166b 100644 --- a/lightning-liquidity/tests/common/mod.rs +++ b/lightning-liquidity/tests/common/mod.rs @@ -469,10 +469,10 @@ 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), + send_only_message_handler: Arc::clone(&chain_monitor), }; let peer_manager = Arc::new(PeerManager::new(msg_handler, 0, &seed, logger.clone(), keys_manager.clone())); diff --git a/lightning/src/chain/chainmonitor.rs b/lightning/src/chain/chainmonitor.rs index 096910d5b01..ceaf54d7fac 100644 --- a/lightning/src/chain/chainmonitor.rs +++ b/lightning/src/chain/chainmonitor.rs @@ -258,6 +258,8 @@ pub struct ChainMonitor>, our_peerstorage_encryption_key: PeerStorageKey, @@ -397,12 +399,16 @@ where C::Target: chain::Filter, /// transactions relevant to the watched channels. /// /// # Note - /// `our_peerstorage_encryption_key` must be obtained from [`crate::sign::NodeSigner::get_peer_storage_key()`]. + /// `our_peerstorage_encryption_key` must be obtained from [`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. + /// is obtained by the [`ChannelManager`] through [`KeysManager`] to decrypt peer backups. /// Using an inconsistent or incorrect key will result in the inability to decrypt previously encrypted backups. + /// + /// [`NodeSigner::get_peer_storage_key`]: crate::sign::NodeSigner::get_peer_storage_key + /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager + /// [`KeysManager`]: crate::sign::KeysManager 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()), From 34e97f7276f0ffeb17641de42bd2a2a5ab683747 Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Mon, 14 Apr 2025 08:45:29 +0530 Subject: [PATCH 17/19] fixup: Distribute PeerStorage from ChainMonitor --- lightning/src/chain/chainmonitor.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lightning/src/chain/chainmonitor.rs b/lightning/src/chain/chainmonitor.rs index ceaf54d7fac..b6d66352995 100644 --- a/lightning/src/chain/chainmonitor.rs +++ b/lightning/src/chain/chainmonitor.rs @@ -710,10 +710,16 @@ where C::Target: chain::Filter, 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() } }) + let our_peer_storage = OurPeerStorage::create_from_data( + peer_storage_key, serialised_channels, random_bytes + ); + + log_debug!(self.logger, "Sending Peer Storage to {}", log_pubkey!(their_node_id)); + let send_peer_storage_event = MessageSendEvent::SendPeerStorage { + node_id: their_node_id, msg: msgs::PeerStorage { data: our_peer_storage.into_vec() } + }; + + self.pending_send_only_events.lock().unwrap().push(send_peer_storage_event) } } From 8a0dd68679df6aafc40bc14362c24fff847c57a3 Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Mon, 14 Apr 2025 08:45:59 +0530 Subject: [PATCH 18/19] fixup: Handle PeerStorageRetrieval in ChannelManager --- lightning/src/ln/channelmanager.rs | 39 ++++++++++++------------------ 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 6cb487530f3..41bd770fc03 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -8327,33 +8327,26 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ // TODO: Check if have any stale or missing ChannelMonitor. let logger = WithContext::from(&self.logger, Some(counterparty_node_id), None, None); - // `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).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)); - } - } + let decrypted_data = match OurPeerStorage::new(msg.data) + .and_then(|storage| storage.decrypt_our_peer_storage(our_peerstorage_encryption_key)) { + Ok(data) => data, 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]))); + log_debug!( + logger, + "Invalid PeerStorage received from {}", + log_pubkey!(counterparty_node_id) + ); + return Err(MsgHandleErrInternal::from_chan_no_close( + ChannelError::Ignore("Invalid PeerStorageRetrieval message received.".into()), + ChannelId([0; 32]), + )); } + }; + + if decrypted_data.is_empty() { + log_debug!(logger, "Received a peer storage from peer {} with 0 channels.", log_pubkey!(counterparty_node_id)); } Ok(()) From 9cae0a2c9d347be714c68a55ae3c60ce1b514516 Mon Sep 17 00:00:00 2001 From: Aditya Sharma Date: Mon, 14 Apr 2025 08:51:40 +0530 Subject: [PATCH 19/19] fixup: Enable ChainMonitor to distribute PeerStorage --- lightning/src/chain/chainmonitor.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lightning/src/chain/chainmonitor.rs b/lightning/src/chain/chainmonitor.rs index b6d66352995..a5cf9c96d4e 100644 --- a/lightning/src/chain/chainmonitor.rs +++ b/lightning/src/chain/chainmonitor.rs @@ -987,12 +987,12 @@ where C::Target: chain::Filter, } 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, +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. ///