Serialise ChannelMonitors and send them over inside Peer Storage
What changed, and why it matters
This commit adds an experimental feature (gated behind a special compile-time flag) that serializes sensitive Lightning channel backup data and sends it to peers for storage. The code itself is a work-in-progress: the authors explicitly note they are unsure which sensitive fields should be omitted from the backup, and the feature is disabled by default. There is no direct vulnerability in the diff, but it introduces a new attack surface where a bug or misconfiguration could leak channel secrets to counterparty peers.
Treat this as a feature-in-development rather than an immediate vulnerability. Reviewers should ensure the final peer-storage serialization omits private key material, revocation secrets, and any data that could allow a counterparty to steal funds or violate privacy. Keep the feature cfg-gated and behind thorough tests until the field-selection TODO is resolved.
Security signals we found
New serialization path for ChannelMonitor data shared with peers
Feature explicitly flagged as experimental/incomplete by vendor comments
Potential over-sharing of sensitive channel state if fields are not later restricted
Encryption is applied before network transmission, reducing passive-leak risk
No input validation or peer-authentication changes visible in this diff
Evidence from the diff
The change refactors ChannelMonitor serialization into a shared write_chanmon_internal() helper and uses it inside ChainMonitor::send_peer_storage() to serialize ChannelMonitors into encrypted peer-storage messages. The entire sending logic is cfg-gated with #[cfg(peer_storage)] and is not enabled in normal builds. The commit message and comments state that the team has not yet decided which ChannelMonitor fields should be included or omitted for peer storage, indicating the feature is incomplete and potentially over-inclusive. The data is encrypted with our_peerstorage_encryption_key before transmission.
Changed components
lightning/src/chain/chainmonitor.rslightning/src/chain/channelmonitor.rsCargo.toml peer_storage cfg flagci/ci-tests.sh peer_storage test jobInspect captured patch +308 / −200
diff --git a/Cargo.toml b/Cargo.toml
index 340e1f2..b89127b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -67,4 +67,5 @@ check-cfg = [
"cfg(require_route_graph_test)",
"cfg(splicing)",
"cfg(simple_close)",
+ "cfg(peer_storage)",
]
diff --git a/ci/ci-tests.sh b/ci/ci-tests.sh
index 2ab512e..1c8a536 100755
--- a/ci/ci-tests.sh
+++ b/ci/ci-tests.sh
@@ -158,3 +158,5 @@ RUSTFLAGS="--cfg=async_payments" cargo test --verbose --color always -p lightnin
RUSTFLAGS="--cfg=simple_close" cargo test --verbose --color always -p lightning
[ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
RUSTFLAGS="--cfg=lsps1_service" cargo test --verbose --color always -p lightning-liquidity
+[ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
+RUSTFLAGS="--cfg=peer_storage" cargo test --verbose --color always -p lightning
diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs
index 3a5a549..ee7f01a 100644
--- a/fuzz/src/full_stack.rs
+++ b/fuzz/src/full_stack.rs
@@ -1182,7 +1182,7 @@ fn two_peer_forwarding_seed() -> Vec<u8> {
// broadcast funding transaction
ext_from_hex("0b", &mut test);
- // by now client should have sent a channel_ready (CHECK 4: SendChannelReady to 03020000 for chan 2f000000)
+ // by now client should have sent a channel_ready (CHECK 4: SendChannelReady to 03020000 for chan 3f000000)
// inbound read from peer id 1 of len 18
ext_from_hex("030112", &mut test);
@@ -1441,7 +1441,7 @@ fn two_peer_forwarding_seed() -> Vec<u8> {
// inbound read from peer id 0 of len 193
ext_from_hex("0300c1", &mut test);
// end of update_add_htlc from 0 to 1 via client and mac
- ext_from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 5200000000000000000000000000000000000000000000000000000000000000 03000000000000000000000000000000", &mut test);
+ ext_from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 5300000000000000000000000000000000000000000000000000000000000000 03000000000000000000000000000000", &mut test);
// inbound read from peer id 0 of len 18
ext_from_hex("030012", &mut test);
diff --git a/lightning/src/chain/chainmonitor.rs b/lightning/src/chain/chainmonitor.rs
index 973692d..386ef0a 100644
--- a/lightning/src/chain/chainmonitor.rs
+++ b/lightning/src/chain/chainmonitor.rs
@@ -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,
@@ -36,8 +38,11 @@ use crate::chain::transaction::{OutPoint, TransactionData};
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;
+#[cfg(peer_storage)]
+use crate::ln::msgs::PeerStorage;
+use crate::ln::msgs::{BaseMessageHandler, Init, MessageSendEvent, SendOnlyMessageHandler};
+#[cfg(peer_storage)]
+use crate::ln::our_peer_storage::{DecryptedOurPeerStorage, PeerStorageMonitorHolder};
use crate::ln::types::ChannelId;
use crate::prelude::*;
use crate::sign::ecdsa::EcdsaChannelSigner;
@@ -47,8 +52,12 @@ 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;
+#[cfg(peer_storage)]
+use core::iter::Cycle;
use core::ops::Deref;
use core::sync::atomic::{AtomicUsize, Ordering};
@@ -264,7 +273,7 @@ pub struct ChainMonitor<
logger: L,
fee_estimator: F,
persister: P,
- entropy_source: ES,
+ _entropy_source: ES,
/// "User-provided" (ie persistence-completion/-failed) [`MonitorEvent`]s. These came directly
/// from the user and not from a [`ChannelMonitor`].
pending_monitor_events: Mutex<Vec<(OutPoint, ChannelId, Vec<MonitorEvent>, PublicKey)>>,
@@ -278,6 +287,7 @@ pub struct ChainMonitor<
/// Messages to send to the peer. This is currently used to distribute PeerStorage to channel partners.
pending_send_only_events: Mutex<Vec<MessageSendEvent>>,
+ #[cfg(peer_storage)]
our_peerstorage_encryption_key: PeerStorageKey,
}
@@ -477,7 +487,7 @@ where
/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
pub fn new(
chain_source: Option<C>, broadcaster: T, logger: L, feeest: F, persister: P,
- entropy_source: ES, our_peerstorage_encryption_key: PeerStorageKey,
+ _entropy_source: ES, _our_peerstorage_encryption_key: PeerStorageKey,
) -> Self {
Self {
monitors: RwLock::new(new_hash_map()),
@@ -486,12 +496,13 @@ where
logger,
fee_estimator: feeest,
persister,
- entropy_source,
+ _entropy_source,
pending_monitor_events: Mutex::new(Vec::new()),
highest_chain_height: AtomicUsize::new(0),
event_notifier: Notifier::new(),
pending_send_only_events: Mutex::new(Vec::new()),
- our_peerstorage_encryption_key,
+ #[cfg(peer_storage)]
+ our_peerstorage_encryption_key: _our_peerstorage_encryption_key,
}
}
@@ -804,23 +815,90 @@ 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()
}
+ #[cfg(peer_storage)]
fn send_peer_storage(&self, their_node_id: PublicKey) {
- // TODO: Serialize `ChannelMonitor`s inside `our_peer_storage`.
+ 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 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 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 let Some(channel_id) = next_random_id(&mut channel_ids, &mut random_bytes_cycle_iter)
+ {
+ 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 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 inner_lock = monitor_holder.monitor.inner.lock().unwrap();
+
+ 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,
+ min_seen_secret,
+ counterparty_node_id,
+ monitor_bytes: serialized_channel.0,
+ };
+
+ let serialized_length = peer_storage_monitor.serialized_length();
+
+ if current_size + serialized_length > MAX_PEER_STORAGE_SIZE {
+ continue;
+ } else {
+ current_size += serialized_length;
+ monitors_list.push(peer_storage_monitor);
+ }
+ }
- let random_bytes = self.entropy_source.get_secure_random_bytes();
- let serialised_channels = Vec::new();
+ 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);
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: cipher.into_vec() },
+ msg: PeerStorage { data: cipher.into_vec() },
};
self.pending_send_only_events.lock().unwrap().push(send_peer_storage_event)
@@ -920,6 +998,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 +1100,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);
diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index aca3bd5..d46cfaa 100644
--- a/lightning/src/chain/channelmonitor.rs
+++ b/lightning/src/chain/channelmonitor.rs
@@ -1402,230 +1402,255 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitor<Signer> {
const SERIALIZATION_VERSION: u8 = 1;
const MIN_SERIALIZATION_VERSION: u8 = 1;
-impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
- fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
- write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
-
- self.latest_update_id.write(writer)?;
-
- // Set in initial Channel-object creation, so should always be set by now:
- U48(self.commitment_transaction_number_obscure_factor).write(writer)?;
-
- self.destination_script.write(writer)?;
- if let Some(ref broadcasted_holder_revokable_script) =
- self.broadcasted_holder_revokable_script
- {
- writer.write_all(&[0; 1])?;
- broadcasted_holder_revokable_script.0.write(writer)?;
- broadcasted_holder_revokable_script.1.write(writer)?;
- broadcasted_holder_revokable_script.2.write(writer)?;
- } else {
- writer.write_all(&[1; 1])?;
- }
-
- self.counterparty_payment_script.write(writer)?;
- match &self.shutdown_script {
- Some(script) => script.write(writer)?,
- None => ScriptBuf::new().write(writer)?,
- }
+/// Utility function for writing [`ChannelMonitor`] to prevent code duplication in [`ChainMonitor`] while sending Peer Storage.
+///
+/// NOTE: `is_stub` is true only when we are using this to serialise for Peer Storage.
+///
+/// TODO: Determine which fields of each `ChannelMonitor` should be included in Peer Storage, and which should be omitted.
+///
+/// [`ChainMonitor`]: crate::chain::chainmonitor::ChainMonitor
+pub(crate) fn write_chanmon_internal<Signer: EcdsaChannelSigner, W: Writer>(
+ channel_monitor: &ChannelMonitorImpl<Signer>, _is_stub: bool, writer: &mut W,
+) -> Result<(), Error> {
+ write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
- self.channel_keys_id.write(writer)?;
- self.holder_revocation_basepoint.write(writer)?;
- let funding_outpoint = self.get_funding_txo();
- writer.write_all(&funding_outpoint.txid[..])?;
- writer.write_all(&funding_outpoint.index.to_be_bytes())?;
- let redeem_script = self.funding.channel_parameters.make_funding_redeemscript();
- let script_pubkey = redeem_script.to_p2wsh();
- script_pubkey.write(writer)?;
- self.funding.current_counterparty_commitment_txid.write(writer)?;
- self.funding.prev_counterparty_commitment_txid.write(writer)?;
-
- self.counterparty_commitment_params.write(writer)?;
- redeem_script.write(writer)?;
- self.funding.channel_parameters.channel_value_satoshis.write(writer)?;
+ channel_monitor.latest_update_id.write(writer)?;
- match self.their_cur_per_commitment_points {
- Some((idx, pubkey, second_option)) => {
- writer.write_all(&byte_utils::be48_to_array(idx))?;
- writer.write_all(&pubkey.serialize())?;
- match second_option {
- Some(second_pubkey) => {
- writer.write_all(&second_pubkey.serialize())?;
- },
- None => {
- writer.write_all(&[0; 33])?;
- },
- }
- },
- None => {
- writer.write_all(&byte_utils::be48_to_array(0))?;
- },
- }
+ // Set in initial Channel-object creation, so should always be set by now:
+ U48(channel_monitor.commitment_transaction_number_obscure_factor).write(writer)?;
- writer.write_all(&self.on_holder_tx_csv.to_be_bytes())?;
+ channel_monitor.destination_script.write(writer)?;
+ if let Some(ref broadcasted_holder_revokable_script) =
+ channel_monitor.broadcasted_holder_revokable_script
+ {
+ writer.write_all(&[0; 1])?;
+ broadcasted_holder_revokable_script.0.write(writer)?;
+ broadcasted_holder_revokable_script.1.write(writer)?;
+ broadcasted_holder_revokable_script.2.write(writer)?;
+ } else {
+ writer.write_all(&[1; 1])?;
+ }
+
+ channel_monitor.counterparty_payment_script.write(writer)?;
+ match &channel_monitor.shutdown_script {
+ Some(script) => script.write(writer)?,
+ None => ScriptBuf::new().write(writer)?,
+ }
+
+ channel_monitor.channel_keys_id.write(writer)?;
+ channel_monitor.holder_revocation_basepoint.write(writer)?;
+ let funding_outpoint = channel_monitor.get_funding_txo();
+ writer.write_all(&funding_outpoint.txid[..])?;
+ writer.write_all(&funding_outpoint.index.to_be_bytes())?;
+ let redeem_script = channel_monitor.funding.channel_parameters.make_funding_redeemscript();
+ let script_pubkey = redeem_script.to_p2wsh();
+ script_pubkey.write(writer)?;
+ channel_monitor.funding.current_counterparty_commitment_txid.write(writer)?;
+ channel_monitor.funding.prev_counterparty_commitment_txid.write(writer)?;
+
+ channel_monitor.counterparty_commitment_params.write(writer)?;
+ redeem_script.write(writer)?;
+ channel_monitor.funding.channel_parameters.channel_value_satoshis.write(writer)?;
+
+ match channel_monitor.their_cur_per_commitment_points {
+ Some((idx, pubkey, second_option)) => {
+ writer.write_all(&byte_utils::be48_to_array(idx))?;
+ writer.write_all(&pubkey.serialize())?;
+ match second_option {
+ Some(second_pubkey) => {
+ writer.write_all(&second_pubkey.serialize())?;
+ },
+ None => {
+ writer.write_all(&[0; 33])?;
+ },
+ }
+ },
+ None => {
+ writer.write_all(&byte_utils::be48_to_array(0))?;
+ },
+ }
- self.commitment_secrets.write(writer)?;
+ writer.write_all(&channel_monitor.on_holder_tx_csv.to_be_bytes())?;
- #[rustfmt::skip]
- macro_rules! serialize_htlc_in_commitment {
- ($htlc_output: expr) => {
- writer.write_all(&[$htlc_output.offered as u8; 1])?;
- writer.write_all(&$htlc_output.amount_msat.to_be_bytes())?;
- writer.write_all(&$htlc_output.cltv_expiry.to_be_bytes())?;
- writer.write_all(&$htlc_output.payment_hash.0[..])?;
- $htlc_output.transaction_output_index.write(writer)?;
- }
- }
+ channel_monitor.commitment_secrets.write(writer)?;
- writer.write_all(
- &(self.funding.counterparty_claimable_outpoints.len() as u64).to_be_bytes(),
- )?;
- for (ref txid, ref htlc_infos) in self.funding.counterparty_claimable_outpoints.iter() {
- writer.write_all(&txid[..])?;
- writer.write_all(&(htlc_infos.len() as u64).to_be_bytes())?;
- for &(ref htlc_output, ref htlc_source) in htlc_infos.iter() {
- debug_assert!(
- htlc_source.is_none()
- || Some(**txid) == self.funding.current_counterparty_commitment_txid
- || Some(**txid) == self.funding.prev_counterparty_commitment_txid,
- "HTLC Sources for all revoked commitment transactions should be none!"
- );
- serialize_htlc_in_commitment!(htlc_output);
- htlc_source.as_ref().map(|b| b.as_ref()).write(writer)?;
- }
+ #[rustfmt::skip]
+ macro_rules! serialize_htlc_in_commitment {
+ ($htlc_output: expr) => {
+ writer.write_all(&[$htlc_output.offered as u8; 1])?;
+ writer.write_all(&$htlc_output.amount_msat.to_be_bytes())?;
+ writer.write_all(&$htlc_output.cltv_expiry.to_be_bytes())?;
+ writer.write_all(&$htlc_output.payment_hash.0[..])?;
+ $htlc_output.transaction_output_index.write(writer)?;
}
+ }
- writer
- .write_all(&(self.counterparty_commitment_txn_on_chain.len() as u64).to_be_bytes())?;
- for (ref txid, commitment_number) in self.counterparty_commitment_txn_on_chain.iter() {
- writer.write_all(&txid[..])?;
- writer.write_all(&byte_utils::be48_to_array(*commitment_number))?;
+ writer.write_all(
+ &(channel_monitor.funding.counterparty_claimable_outpoints.len() as u64).to_be_bytes(),
+ )?;
+ for (ref txid, ref htlc_infos) in
+ channel_monitor.funding.counterparty_claimable_outpoints.iter()
+ {
+ writer.write_all(&txid[..])?;
+ writer.write_all(&(htlc_infos.len() as u64).to_be_bytes())?;
+ for &(ref htlc_output, ref htlc_source) in htlc_infos.iter() {
+ debug_assert!(
+ htlc_source.is_none()
+ || Some(**txid) == channel_monitor.funding.current_counterparty_commitment_txid
+ || Some(**txid) == channel_monitor.funding.prev_counterparty_commitment_txid,
+ "HTLC Sources for all revoked commitment transactions should be none!"
+ );
+ serialize_htlc_in_commitment!(htlc_output);
+ htlc_source.as_ref().map(|b| b.as_ref()).write(writer)?;
}
+ }
- writer.write_all(&(self.counterparty_hash_commitment_number.len() as u64).to_be_bytes())?;
- for (ref payment_hash, commitment_number) in self.counterparty_hash_commitment_number.iter()
- {
- writer.write_all(&payment_hash.0[..])?;
- writer.write_all(&byte_utils::be48_to_array(*commitment_number))?;
- }
+ writer.write_all(
+ &(channel_monitor.counterparty_commitment_txn_on_chain.len() as u64).to_be_bytes(),
+ )?;
+ for (ref txid, commitment_number) in channel_monitor.counterparty_commitment_txn_on_chain.iter()
+ {
+ writer.write_all(&txid[..])?;
+ writer.write_all(&byte_utils::be48_to_array(*commitment_number))?;
+ }
- if let Some(holder_commitment_tx) = &self.funding.prev_holder_commitment_tx {
- writer.write_all(&[1; 1])?;
- write_legacy_holder_commitment_data(
- writer,
- holder_commitment_tx,
- &self.prev_holder_htlc_data.as_ref().unwrap(),
- )?;
- } else {
- writer.write_all(&[0; 1])?;
- }
+ writer.write_all(
+ &(channel_monitor.counterparty_hash_commitment_number.len() as u64).to_be_bytes(),
+ )?;
+ for (ref payment_hash, commitment_number) in
+ channel_monitor.counterparty_hash_commitment_number.iter()
+ {
+ writer.write_all(&payment_hash.0[..])?;
+ writer.write_all(&byte_utils::be48_to_array(*commitment_number))?;
+ }
+ if let Some(holder_commitment_tx) = &channel_monitor.funding.prev_holder_commitment_tx {
+ writer.write_all(&[1; 1])?;
write_legacy_holder_commitment_data(
writer,
- &self.funding.current_holder_commitment_tx,
- &self.current_holder_htlc_data,
+ holder_commitment_tx,
+ &channel_monitor.prev_holder_htlc_data.as_ref().unwrap(),
)?;
+ } else {
+ writer.write_all(&[0; 1])?;
+ }
- writer
- .write_all(&byte_utils::be48_to_array(self.current_counterparty_commitment_number))?;
- writer.write_all(&byte_utils::be48_to_array(self.current_holder_commitment_number))?;
+ write_legacy_holder_commitment_data(
+ writer,
+ &channel_monitor.funding.current_holder_commitment_tx,
+ &channel_monitor.current_holder_htlc_data,
+ )?;
- writer.write_all(&(self.payment_preimages.len() as u64).to_be_bytes())?;
- for (payment_preimage, _) in self.payment_preimages.values() {
- writer.write_all(&payment_preimage.0[..])?;
- }
+ writer.write_all(&byte_utils::be48_to_array(
+ channel_monitor.current_counterparty_commitment_number,
+ ))?;
+ writer
+ .write_all(&byte_utils::be48_to_array(channel_monitor.current_holder_commitment_number))?;
- writer.write_all(
- &(self
- .pending_monitor_events
- .iter()
- .filter(|ev| match ev {
- MonitorEvent::HTLCEvent(_) => true,
- MonitorEvent::HolderForceClosed(_) => true,
- MonitorEvent::HolderForceClosedWithInfo { .. } => true,
- _ => false,
- })
- .count() as u64)
- .to_be_bytes(),
- )?;
- for event in self.pending_monitor_events.iter() {
- match event {
- MonitorEvent::HTLCEvent(upd) => {
- 0u8.write(writer)?;
- upd.write(writer)?;
- },
- MonitorEvent::HolderForceClosed(_) => 1u8.write(writer)?,
- // `HolderForceClosedWithInfo` replaced `HolderForceClosed` in v0.0.122. To keep
- // backwards compatibility, we write a `HolderForceClosed` event along with the
- // `HolderForceClosedWithInfo` event. This is deduplicated in the reader.
- MonitorEvent::HolderForceClosedWithInfo { .. } => 1u8.write(writer)?,
- _ => {}, // Covered in the TLV writes below
- }
- }
+ writer.write_all(&(channel_monitor.payment_preimages.len() as u64).to_be_bytes())?;
+ for (payment_preimage, _) in channel_monitor.payment_preimages.values() {
+ writer.write_all(&payment_preimage.0[..])?;
+ }
- writer.write_all(&(self.pending_events.len() as u64).to_be_bytes())?;
- for event in self.pending_events.iter() {
- event.write(writer)?;
+ writer.write_all(
+ &(channel_monitor
+ .pending_monitor_events
+ .iter()
+ .filter(|ev| match ev {
+ MonitorEvent::HTLCEvent(_) => true,
+ MonitorEvent::HolderForceClosed(_) => true,
+ MonitorEvent::HolderForceClosedWithInfo { .. } => true,
+ _ => false,
+ })
+ .count() as u64)
+ .to_be_bytes(),
+ )?;
+ for event in channel_monitor.pending_monitor_events.iter() {
+ match event {
+ MonitorEvent::HTLCEvent(upd) => {
+ 0u8.write(writer)?;
+ upd.write(writer)?;
+ },
+ MonitorEvent::HolderForceClosed(_) => 1u8.write(writer)?,
+ // `HolderForceClosedWithInfo` replaced `HolderForceClosed` in v0.0.122. To keep
+ // backwards compatibility, we write a `HolderForceClosed` event along with the
+ // `HolderForceClosedWithInfo` event. This is deduplicated in the reader.
+ MonitorEvent::HolderForceClosedWithInfo { .. } => 1u8.write(writer)?,
+ _ => {}, // Covered in the TLV writes below
}
+ }
- self.best_block.block_hash.write(writer)?;
- writer.write_all(&self.best_block.height.to_be_bytes())?;
+ writer.write_all(&(channel_monitor.pending_events.len() as u64).to_be_bytes())?;
+ for event in channel_monitor.pending_events.iter() {
+ event.write(writer)?;
+ }
- writer
- .write_all(&(self.onchain_events_awaiting_threshold_conf.len() as u64).to_be_bytes())?;
- for ref entry in self.onchain_events_awaiting_threshold_conf.iter() {
- entry.write(writer)?;
- }
+ channel_monitor.best_block.block_hash.write(writer)?;
+ writer.write_all(&channel_monitor.best_block.height.to_be_bytes())?;
- (self.outputs_to_watch.len() as u64).write(writer)?;
- for (txid, idx_scripts) in self.outputs_to_watch.iter() {
- txid.write(writer)?;
- (idx_scripts.len() as u64).write(writer)?;
- for (idx, script) in idx_scripts.iter() {
- idx.write(writer)?;
- script.write(writer)?;
- }
+ writer.write_all(
+ &(channel_monitor.onchain_events_awaiting_threshold_conf.len() as u64).to_be_bytes(),
+ )?;
+ for ref entry in channel_monitor.onchain_events_awaiting_threshold_conf.iter() {
+ entry.write(writer)?;
+ }
+
+ (channel_monitor.outputs_to_watch.len() as u64).write(writer)?;
+ for (txid, idx_scripts) in channel_monitor.outputs_to_watch.iter() {
+ txid.write(writer)?;
+ (idx_scripts.len() as u64).write(writer)?;
+ for (idx, script) in idx_scripts.iter() {
+ idx.write(writer)?;
+ script.write(writer)?;
}
- self.onchain_tx_handler.write(writer)?;
+ }
- self.lockdown_from_offchain.write(writer)?;
- self.holder_tx_signed.write(writer)?;
+ channel_monitor.onchain_tx_handler.write(writer)?;
- // If we have a `HolderForceClosedWithInfo` event, we need to write the `HolderForceClosed` for backwards compatibility.
- let pending_monitor_events = match self.pending_monitor_events.iter().find(|ev| match ev {
+ channel_monitor.lockdown_from_offchain.write(writer)?;
+ channel_monitor.holder_tx_signed.write(writer)?;
+
+ // If we have a `HolderForceClosedWithInfo` event, we need to write the `HolderForceClosed` for backwards compatibility.
+ let pending_monitor_events =
+ match channel_monitor.pending_monitor_events.iter().find(|ev| match ev {
MonitorEvent::HolderForceClosedWithInfo { .. } => true,
_ => false,
}) {
Some(MonitorEvent::HolderForceClosedWithInfo { outpoint, .. }) => {
- let mut pending_monitor_events = self.pending_monitor_events.clone();
+ let mut pending_monitor_events = channel_monitor.pending_monitor_events.clone();
pending_monitor_events.push(MonitorEvent::HolderForceClosed(*outpoint));
pending_monitor_events
},
- _ => self.pending_monitor_events.clone(),
+ _ => channel_monitor.pending_monitor_events.clone(),
};
- write_tlv_fields!(writer, {
- (1, self.funding_spend_confirmed, option),
- (3, self.htlcs_resolved_on_chain, required_vec),
- (5, pending_monitor_events, required_vec),
- (7, self.funding_spend_seen, required),
- (9, self.counterparty_node_id, required),
- (11, self.confirmed_commitment_tx_counterparty_output, option),
- (13, self.spendable_txids_confirmed, required_vec),
- (15, self.counterparty_fulfilled_htlcs, required),
- (17, self.initial_counterparty_commitment_info, option),
- (19, self.channel_id, required),
- (21, self.balances_empty_height, option),
- (23, self.holder_pays_commitment_tx_fee, option),
- (25, self.payment_preimages, required),
- (27, self.first_negotiated_funding_txo, required),
- (29, self.initial_counterparty_commitment_tx, option),
- (31, self.funding.channel_parameters, required),
- (32, self.pending_funding, optional_vec),
- (34, self.alternative_funding_confirmed, option),
- });
+ write_tlv_fields!(writer, {
+ (1, channel_monitor.funding_spend_confirmed, option),
+ (3, channel_monitor.htlcs_resolved_on_chain, required_vec),
+ (5, pending_monitor_events, required_vec),
+ (7, channel_monitor.funding_spend_seen, required),
+ (9, channel_monitor.counterparty_node_id, required),
+ (11, channel_monitor.confirmed_commitment_tx_counterparty_output, option),
+ (13, channel_monitor.spendable_txids_confirmed, required_vec),
+ (15, channel_monitor.counterparty_fulfilled_htlcs, required),
+ (17, channel_monitor.initial_counterparty_commitment_info, option),
+ (19, channel_monitor.channel_id, required),
+ (21, channel_monitor.balances_empty_height, option),
+ (23, channel_monitor.holder_pays_commitment_tx_fee, option),
+ (25, channel_monitor.payment_preimages, required),
+ (27, channel_monitor.first_negotiated_funding_txo, required),
+ (29, channel_monitor.initial_counterparty_commitment_tx, option),
+ (31, channel_monitor.funding.channel_parameters, required),
+ (32, channel_monitor.pending_funding, optional_vec),
+ (34, channel_monitor.alternative_funding_confirmed, option),
+ });
- Ok(())
+ Ok(())
+}
+
+impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
+ write_chanmon_internal(self, false, writer)
}
}
Why this scored 31/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.