-
Notifications
You must be signed in to change notification settings - Fork 418
Peer Storage (Part 3): Identifying Lost Channel States #3897
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
65b5e2b
28d7874
341fe0a
2b3fdb9
662fff1
8a05784
e1f702f
6328b33
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -68,4 +68,5 @@ check-cfg = [ | |
"cfg(splicing)", | ||
"cfg(async_payments)", | ||
"cfg(simple_close)", | ||
"cfg(peer_storage)", | ||
] |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -28,6 +28,8 @@ use bitcoin::hash_types::{BlockHash, Txid}; | |||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
use crate::chain; | ||||||||||||||||||||||||||||||||||||
use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator}; | ||||||||||||||||||||||||||||||||||||
#[cfg(peer_storage)] | ||||||||||||||||||||||||||||||||||||
use crate::chain::channelmonitor::write_chanmon_internal; | ||||||||||||||||||||||||||||||||||||
use crate::chain::channelmonitor::{ | ||||||||||||||||||||||||||||||||||||
Balance, ChannelMonitor, ChannelMonitorUpdate, MonitorEvent, TransactionOutputs, | ||||||||||||||||||||||||||||||||||||
WithChannelMonitor, | ||||||||||||||||||||||||||||||||||||
|
@@ -37,7 +39,7 @@ use crate::chain::{ChannelMonitorUpdateStatus, Filter, WatchedOutput}; | |||||||||||||||||||||||||||||||||||
use crate::events::{self, Event, EventHandler, ReplayEvent}; | ||||||||||||||||||||||||||||||||||||
use crate::ln::channel_state::ChannelDetails; | ||||||||||||||||||||||||||||||||||||
use crate::ln::msgs::{self, BaseMessageHandler, Init, MessageSendEvent, SendOnlyMessageHandler}; | ||||||||||||||||||||||||||||||||||||
use crate::ln::our_peer_storage::DecryptedOurPeerStorage; | ||||||||||||||||||||||||||||||||||||
use crate::ln::our_peer_storage::{DecryptedOurPeerStorage, PeerStorageMonitorHolder}; | ||||||||||||||||||||||||||||||||||||
use crate::ln::types::ChannelId; | ||||||||||||||||||||||||||||||||||||
use crate::prelude::*; | ||||||||||||||||||||||||||||||||||||
use crate::sign::ecdsa::EcdsaChannelSigner; | ||||||||||||||||||||||||||||||||||||
|
@@ -47,6 +49,8 @@ use crate::types::features::{InitFeatures, NodeFeatures}; | |||||||||||||||||||||||||||||||||||
use crate::util::errors::APIError; | ||||||||||||||||||||||||||||||||||||
use crate::util::logger::{Logger, WithContext}; | ||||||||||||||||||||||||||||||||||||
use crate::util::persist::MonitorName; | ||||||||||||||||||||||||||||||||||||
#[cfg(peer_storage)] | ||||||||||||||||||||||||||||||||||||
use crate::util::ser::{VecWriter, Writeable}; | ||||||||||||||||||||||||||||||||||||
use crate::util::wakers::{Future, Notifier}; | ||||||||||||||||||||||||||||||||||||
use bitcoin::secp256k1::PublicKey; | ||||||||||||||||||||||||||||||||||||
use core::ops::Deref; | ||||||||||||||||||||||||||||||||||||
|
@@ -809,11 +813,57 @@ where | |||||||||||||||||||||||||||||||||||
mon.values().map(|monitor| monitor.monitor.get_counterparty_node_id()).collect() | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
#[cfg(peer_storage)] | ||||||||||||||||||||||||||||||||||||
fn send_peer_storage(&self, their_node_id: PublicKey) { | ||||||||||||||||||||||||||||||||||||
// TODO: Serialize `ChannelMonitor`s inside `our_peer_storage`. | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
#[allow(unused_mut)] | ||||||||||||||||||||||||||||||||||||
let mut monitors_list: Vec<PeerStorageMonitorHolder> = Vec::new(); | ||||||||||||||||||||||||||||||||||||
let random_bytes = self.entropy_source.get_secure_random_bytes(); | ||||||||||||||||||||||||||||||||||||
let serialised_channels = Vec::new(); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
const MAX_PEER_STORAGE_SIZE: usize = 65531; | ||||||||||||||||||||||||||||||||||||
const USIZE_LEN: usize = core::mem::size_of::<usize>(); | ||||||||||||||||||||||||||||||||||||
let mut usize_bytes = [0u8; USIZE_LEN]; | ||||||||||||||||||||||||||||||||||||
usize_bytes.copy_from_slice(&random_bytes[0..USIZE_LEN]); | ||||||||||||||||||||||||||||||||||||
let random_usize = usize::from_le_bytes(usize_bytes); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
let mut curr_size = 0; | ||||||||||||||||||||||||||||||||||||
let monitors = self.monitors.read().unwrap(); | ||||||||||||||||||||||||||||||||||||
let mut stored_chanmon_idx = alloc::collections::BTreeSet::<usize>::new(); | ||||||||||||||||||||||||||||||||||||
// Used as a fallback reference if the set is empty | ||||||||||||||||||||||||||||||||||||
let zero = 0; | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
while curr_size < MAX_PEER_STORAGE_SIZE | ||||||||||||||||||||||||||||||||||||
&& *stored_chanmon_idx.last().unwrap_or(&zero) < monitors.len() | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
let idx = random_usize % monitors.len(); | ||||||||||||||||||||||||||||||||||||
stored_chanmon_idx.insert(idx + 1); | ||||||||||||||||||||||||||||||||||||
Comment on lines
+834
to
+838
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The loop condition has a potential issue where it might not terminate if the same random index is repeatedly selected. Since Consider modifying the approach to ensure termination, such as:
This would prevent potential infinite loops while still achieving the goal of selecting monitors for peer storage.
Suggested change
Spotted by Diamond There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tnull, this is the same concern that I had in my mind as well. Wasn't the previous approach more efficient and secure? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about something along these lines?: diff --git a/lightning/src/chain/chainmonitor.rs b/lightning/src/chain/chainmonitor.rs
index 4a40ba872..dde117ca9 100644
--- a/lightning/src/chain/chainmonitor.rs
+++ b/lightning/src/chain/chainmonitor.rs
@@ -39,6 +39,7 @@ use crate::chain::{ChannelMonitorUpdateStatus, Filter, WatchedOutput};
use crate::events::{self, Event, EventHandler, ReplayEvent};
use crate::ln::channel_state::ChannelDetails;
use crate::ln::msgs::{self, BaseMessageHandler, Init, MessageSendEvent, SendOnlyMessageHandler};
+#[cfg(peer_storage)]
use crate::ln::our_peer_storage::{DecryptedOurPeerStorage, PeerStorageMonitorHolder};
use crate::ln::types::ChannelId;
use crate::prelude::*;
@@ -53,6 +54,8 @@ use crate::util::persist::MonitorName;
use crate::util::ser::{VecWriter, Writeable};
use crate::util::wakers::{Future, Notifier};
use bitcoin::secp256k1::PublicKey;
+#[cfg(peer_storage)]
+use core::iter::Cycle;
use core::ops::Deref;
use core::sync::atomic::{AtomicUsize, Ordering};
@@ -808,6 +811,7 @@ where
/// This function collects the counterparty node IDs from all monitors into a `HashSet`,
/// ensuring unique IDs are returned.
+ #[cfg(peer_storage)]
fn all_counterparty_node_ids(&self) -> HashSet<PublicKey> {
let mon = self.monitors.read().unwrap();
mon.values().map(|monitor| monitor.monitor.get_counterparty_node_id()).collect()
@@ -815,51 +819,71 @@ where
#[cfg(peer_storage)]
fn send_peer_storage(&self, their_node_id: PublicKey) {
- #[allow(unused_mut)]
let mut monitors_list: Vec<PeerStorageMonitorHolder> = Vec::new();
let random_bytes = self.entropy_source.get_secure_random_bytes();
const MAX_PEER_STORAGE_SIZE: usize = 65531;
const USIZE_LEN: usize = core::mem::size_of::<usize>();
- let mut usize_bytes = [0u8; USIZE_LEN];
- usize_bytes.copy_from_slice(&random_bytes[0..USIZE_LEN]);
- let random_usize = usize::from_le_bytes(usize_bytes);
+ let mut random_bytes_cycle_iter = random_bytes.iter().cycle();
+
+ let mut current_size = 0;
+ let monitors_lock = self.monitors.read().unwrap();
+ let mut channel_ids = monitors_lock.keys().copied().collect();
+
+ fn next_random_id(
+ channel_ids: &mut Vec<ChannelId>,
+ random_bytes_cycle_iter: &mut Cycle<core::slice::Iter<u8>>,
+ ) -> Option<ChannelId> {
+ if channel_ids.is_empty() {
+ return None;
+ }
- let mut curr_size = 0;
- let monitors = self.monitors.read().unwrap();
- let mut stored_chanmon_idx = alloc::collections::BTreeSet::<usize>::new();
- // Used as a fallback reference if the set is empty
- let zero = 0;
+ let random_idx = {
+ let mut usize_bytes = [0u8; USIZE_LEN];
+ usize_bytes.iter_mut().for_each(|b| {
+ *b = *random_bytes_cycle_iter.next().expect("A cycle never ends")
+ });
+ // Take one more to introduce a slight misalignment.
+ random_bytes_cycle_iter.next().expect("A cycle never ends");
+ usize::from_le_bytes(usize_bytes) % channel_ids.len()
+ };
+
+ Some(channel_ids.swap_remove(random_idx))
+ }
- while curr_size < MAX_PEER_STORAGE_SIZE
- && *stored_chanmon_idx.last().unwrap_or(&zero) < monitors.len()
+ while let Some(channel_id) = next_random_id(&mut channel_ids, &mut random_bytes_cycle_iter)
{
- let idx = random_usize % monitors.len();
- stored_chanmon_idx.insert(idx + 1);
- let (cid, mon) = monitors.iter().skip(idx).next().unwrap();
+ let monitor_holder = if let Some(monitor_holder) = monitors_lock.get(&channel_id) {
+ monitor_holder
+ } else {
+ debug_assert!(
+ false,
+ "Tried to access non-existing monitor, this should never happen"
+ );
+ break;
+ };
- let mut ser_chan = VecWriter(Vec::new());
- let min_seen_secret = mon.monitor.get_min_seen_secret();
- let counterparty_node_id = mon.monitor.get_counterparty_node_id();
+ let mut serialized_channel = VecWriter(Vec::new());
+ let min_seen_secret = monitor_holder.monitor.get_min_seen_secret();
+ let counterparty_node_id = monitor_holder.monitor.get_counterparty_node_id();
{
- let chan_mon = mon.monitor.inner.lock().unwrap();
+ let inner_lock = monitor_holder.monitor.inner.lock().unwrap();
- write_chanmon_internal(&chan_mon, true, &mut ser_chan)
+ write_chanmon_internal(&inner_lock, true, &mut serialized_channel)
.expect("can not write Channel Monitor for peer storage message");
}
let peer_storage_monitor = PeerStorageMonitorHolder {
- channel_id: *cid,
+ channel_id,
min_seen_secret,
counterparty_node_id,
- monitor_bytes: ser_chan.0,
+ monitor_bytes: serialized_channel.0,
};
- // Adding size of peer_storage_monitor.
- curr_size += peer_storage_monitor.serialized_length();
-
- if curr_size > MAX_PEER_STORAGE_SIZE {
- break;
+ current_size += peer_storage_monitor.serialized_length();
+ if current_size > MAX_PEER_STORAGE_SIZE {
+ continue;
}
+
monitors_list.push(peer_storage_monitor);
} There might be still some room for improvement, but IMO something like this would be a lot more readable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Why not break here? |
||||||||||||||||||||||||||||||||||||
let (cid, mon) = monitors.iter().skip(idx).next().unwrap(); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
let mut ser_chan = VecWriter(Vec::new()); | ||||||||||||||||||||||||||||||||||||
let min_seen_secret = mon.monitor.get_min_seen_secret(); | ||||||||||||||||||||||||||||||||||||
let counterparty_node_id = mon.monitor.get_counterparty_node_id(); | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
let chan_mon = mon.monitor.inner.lock().unwrap(); | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we put this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, thanks for pointing this out. Fixed. |
||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
write_chanmon_internal(&chan_mon, true, &mut ser_chan) | ||||||||||||||||||||||||||||||||||||
.expect("can not write Channel Monitor for peer storage message"); | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
let peer_storage_monitor = PeerStorageMonitorHolder { | ||||||||||||||||||||||||||||||||||||
channel_id: *cid, | ||||||||||||||||||||||||||||||||||||
min_seen_secret, | ||||||||||||||||||||||||||||||||||||
counterparty_node_id, | ||||||||||||||||||||||||||||||||||||
monitor_bytes: ser_chan.0, | ||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
// Adding size of peer_storage_monitor. | ||||||||||||||||||||||||||||||||||||
curr_size += peer_storage_monitor.serialized_length(); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
if curr_size > MAX_PEER_STORAGE_SIZE { | ||||||||||||||||||||||||||||||||||||
break; | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
monitors_list.push(peer_storage_monitor); | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
let serialised_channels = monitors_list.encode(); | ||||||||||||||||||||||||||||||||||||
let our_peer_storage = DecryptedOurPeerStorage::new(serialised_channels); | ||||||||||||||||||||||||||||||||||||
let cipher = our_peer_storage.encrypt(&self.our_peerstorage_encryption_key, &random_bytes); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
|
@@ -920,6 +970,7 @@ where | |||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
#[cfg(peer_storage)] | ||||||||||||||||||||||||||||||||||||
// Send peer storage everytime a new block arrives. | ||||||||||||||||||||||||||||||||||||
for node_id in self.all_counterparty_node_ids() { | ||||||||||||||||||||||||||||||||||||
self.send_peer_storage(node_id); | ||||||||||||||||||||||||||||||||||||
|
@@ -1021,6 +1072,7 @@ where | |||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
#[cfg(peer_storage)] | ||||||||||||||||||||||||||||||||||||
// Send peer storage everytime a new block arrives. | ||||||||||||||||||||||||||||||||||||
for node_id in self.all_counterparty_node_ids() { | ||||||||||||||||||||||||||||||||||||
self.send_peer_storage(node_id); | ||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you'll also need to introduce the
cfg
-gate in a few other places to get rid of the warning, e.g., above forall_counterparty_node_ids
and on some imports.