Add `counterparty_node_id` to `TransactionType` variants
What changed, and why it matters
This commit is a straightforward observability and metadata improvement: it adds the channel counterparty's node ID to every variant of the TransactionType enum used when LDK broadcasts on-chain transactions. It does not change transaction logic, signatures, fee calculations, or who can broadcast what. Downstream users (such as custom transaction broadcasters or monitoring tools) now get extra context about which peer is involved in each transaction. The change includes a backward-compatibility shim that fills in a dummy counterparty ID when loading older serialized data and then overwrites it once the real value is read.
No security action required. Reviewers may want to confirm downstream consumers of TransactionType handle the new enum shape and that the dummy-key deserialization fallback is always overwritten before any broadcast occurs.
Security signals we found
Public API change to TransactionType enum (adds counterparty_node_id to all variants)
Deserialization fallback uses a dummy public key for counterparty_node_id until ChannelMonitor supplies the real one
No change to transaction validity, signature checks, fee logic, or authorization
No memory-safety, cryptographic, or consensus-sensitive code modified
Evidence from the diff
The patch extends the public TransactionType enum in lightning/src/chain/chaininterface.rs so that every variant carries a counterparty_node_id (PublicKey), and the multi-channel Funding/Sweep variants now hold Vec<(PublicKey, ChannelId)> instead of Vec
Changed components
lightning/src/chain/chaininterface.rs (TransactionType enum)lightning/src/chain/onchaintx.rs (OnchainTxHandler field and setter)lightning/src/chain/channelmonitor.rs (deserialization wiring)lightning/src/events/bump_transaction/mod.rs (broadcast type tagging)lightning/src/ln/channel.rs (funding/splice type tagging)lightning/src/ln/channelmanager.rs (batch funding and close type tagging)lightning/src/util/sweep.rs (sweep type tagging)lightning-liquidity/src/lsps2/service.rs (JIT funding type tagging)Inspect captured patch +124 / −56
diff --git a/lightning-liquidity/src/lsps2/service.rs b/lightning-liquidity/src/lsps2/service.rs
index 4c688d3..1909e87 100644
--- a/lightning-liquidity/src/lsps2/service.rs
+++ b/lightning-liquidity/src/lsps2/service.rs
@@ -2020,21 +2020,22 @@ where
// close could then confirm the commitment and trigger unintended on‑chain handling.
// To avoid this, we check ChannelManager’s view (`is_channel_ready`) before broadcasting.
if let Some(ch_id) = jit_channel.get_channel_id() {
- let is_channel_ready = self
+ let channel_details = self
.channel_manager
.get_cm()
.list_channels()
.into_iter()
- .any(|cd| cd.channel_id == ch_id && cd.is_channel_ready);
+ .find(|cd| cd.channel_id == ch_id && cd.is_channel_ready);
- if !is_channel_ready {
- return;
- }
+ let counterparty_node_id = match channel_details {
+ Some(cd) => cd.counterparty.node_id,
+ None => return,
+ };
if let Some(funding_tx) = jit_channel.get_funding_tx() {
self.tx_broadcaster.broadcast_transactions(&[(
funding_tx,
- TransactionType::Funding { channel_ids: vec![ch_id] },
+ TransactionType::Funding { channels: vec![(counterparty_node_id, ch_id)] },
)]);
}
}
diff --git a/lightning/src/chain/chaininterface.rs b/lightning/src/chain/chaininterface.rs
index 758fd1a..806e947 100644
--- a/lightning/src/chain/chaininterface.rs
+++ b/lightning/src/chain/chaininterface.rs
@@ -18,6 +18,7 @@ use core::{cmp, ops::Deref};
use crate::ln::types::ChannelId;
use crate::prelude::*;
+use bitcoin::secp256k1::PublicKey;
use bitcoin::transaction::Transaction;
/// Represents the class of transaction being broadcast.
@@ -33,10 +34,10 @@ pub enum TransactionType {
///
/// [`ChannelManager::funding_transaction_generated`]: crate::ln::channelmanager::ChannelManager::funding_transaction_generated
Funding {
- /// The IDs of the channels being funded.
+ /// The counterparty node IDs and channel IDs of the channels being funded.
///
/// A single funding transaction may establish multiple channels when using batch funding.
- channel_ids: Vec<ChannelId>,
+ channels: Vec<(PublicKey, ChannelId)>,
},
/// A transaction cooperatively closing a channel.
///
@@ -45,6 +46,8 @@ pub enum TransactionType {
///
/// [`ChannelManager::close_channel`]: crate::ln::channelmanager::ChannelManager::close_channel
CooperativeClose {
+ /// The `node_id` of the channel counterparty.
+ counterparty_node_id: PublicKey,
/// The ID of the channel being closed.
channel_id: ChannelId,
},
@@ -56,6 +59,8 @@ pub enum TransactionType {
///
/// [`ChannelManager::force_close_broadcasting_latest_txn`]: crate::ln::channelmanager::ChannelManager::force_close_broadcasting_latest_txn
UnilateralClose {
+ /// The `node_id` of the channel counterparty.
+ counterparty_node_id: PublicKey,
/// The ID of the channel being force-closed.
channel_id: ChannelId,
},
@@ -66,6 +71,8 @@ pub enum TransactionType {
///
/// [`BumpTransactionEvent`]: crate::events::bump_transaction::BumpTransactionEvent
AnchorBump {
+ /// The `node_id` of the channel counterparty.
+ counterparty_node_id: PublicKey,
/// The ID of the channel whose closing transaction is being fee-bumped.
channel_id: ChannelId,
},
@@ -81,6 +88,8 @@ pub enum TransactionType {
/// [`ChannelMonitor`]: crate::chain::ChannelMonitor
/// [`Event::SpendableOutputs`]: crate::events::Event::SpendableOutputs
Claim {
+ /// The `node_id` of the channel counterparty.
+ counterparty_node_id: PublicKey,
/// The ID of the channel from which outputs are being claimed.
channel_id: ChannelId,
},
@@ -90,10 +99,10 @@ pub enum TransactionType {
/// [`OutputSweeper`]: crate::util::sweep::OutputSweeper
/// [`SpendableOutputDescriptor`]: crate::sign::SpendableOutputDescriptor
Sweep {
- /// The IDs of the channels from which outputs are being swept, if known.
+ /// The counterparty node IDs and channel IDs from which outputs are being swept, if known.
///
/// A single sweep transaction may aggregate outputs from multiple channels.
- channel_ids: Vec<ChannelId>,
+ channels: Vec<(PublicKey, ChannelId)>,
},
/// A splice transaction modifying an existing channel's funding.
///
@@ -101,6 +110,8 @@ pub enum TransactionType {
///
/// [`ChannelManager::splice_channel`]: crate::ln::channelmanager::ChannelManager::splice_channel
Splice {
+ /// The `node_id` of the channel counterparty.
+ counterparty_node_id: PublicKey,
/// The ID of the channel being spliced.
channel_id: ChannelId,
},
diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs
index b109466..6205fa8 100644
--- a/lightning/src/chain/channelmonitor.rs
+++ b/lightning/src/chain/channelmonitor.rs
@@ -1879,8 +1879,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
initial_holder_commitment_tx.trust().commitment_number();
let onchain_tx_handler = OnchainTxHandler::new(
- channel_id, channel_parameters.channel_value_satoshis, channel_keys_id,
- destination_script.into(), keys, channel_parameters.clone(),
+ channel_id, counterparty_node_id, channel_parameters.channel_value_satoshis,
+ channel_keys_id, destination_script.into(), keys, channel_parameters.clone(),
initial_holder_commitment_tx.clone(), secp_ctx,
);
@@ -6644,6 +6644,8 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
};
let dummy_node_id = PublicKey::from_slice(&[2; 33]).unwrap();
+ onchain_tx_handler
+ .set_counterparty_node_id(counterparty_node_id.unwrap_or(dummy_node_id));
let monitor = ChannelMonitor::from_impl(ChannelMonitorImpl {
funding: FundingScope {
channel_parameters,
diff --git a/lightning/src/chain/onchaintx.rs b/lightning/src/chain/onchaintx.rs
index 8de99eb..3eb6d64 100644
--- a/lightning/src/chain/onchaintx.rs
+++ b/lightning/src/chain/onchaintx.rs
@@ -18,7 +18,7 @@ use bitcoin::hashes::Hash;
use bitcoin::locktime::absolute::LockTime;
use bitcoin::script::{Script, ScriptBuf};
use bitcoin::secp256k1;
-use bitcoin::secp256k1::{ecdsa::Signature, Secp256k1};
+use bitcoin::secp256k1::{ecdsa::Signature, PublicKey, Secp256k1};
use bitcoin::transaction::OutPoint as BitcoinOutPoint;
use bitcoin::transaction::Transaction;
@@ -224,6 +224,7 @@ pub(crate) enum FeerateStrategy {
#[derive(Clone)]
pub struct OnchainTxHandler<ChannelSigner: EcdsaChannelSigner> {
channel_id: ChannelId,
+ counterparty_node_id: PublicKey,
channel_value_satoshis: u64, // Deprecated as of 0.2.
channel_keys_id: [u8; 32], // Deprecated as of 0.2.
destination_script: ScriptBuf, // Deprecated as of 0.2.
@@ -287,6 +288,7 @@ impl<ChannelSigner: EcdsaChannelSigner> PartialEq for OnchainTxHandler<ChannelSi
fn eq(&self, other: &Self) -> bool {
// `signer`, `secp_ctx`, and `pending_claim_events` are excluded on purpose.
self.channel_id == other.channel_id &&
+ self.counterparty_node_id == other.counterparty_node_id &&
self.channel_value_satoshis == other.channel_value_satoshis &&
self.channel_keys_id == other.channel_keys_id &&
self.destination_script == other.destination_script &&
@@ -358,6 +360,14 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
pub(crate) fn set_channel_id(&mut self, channel_id: ChannelId) {
self.channel_id = channel_id;
}
+
+ // `ChannelMonitor`s already track the `counterparty_node_id`, however, due to the
+ // deserialization order there we can't make use of `ReadableArgs` to hand it into
+ // `OnchainTxHandler`'s deserialization logic directly. Instead we opt to initialize it with a
+ // dummy key and override it after reading the respective field via this method.
+ pub(crate) fn set_counterparty_node_id(&mut self, counterparty_node_id: PublicKey) {
+ self.counterparty_node_id = counterparty_node_id;
+ }
}
impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP, u64, [u8; 32])>
@@ -433,17 +443,20 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
read_tlv_fields!(reader, {});
- // `ChannelMonitor`s already track the `channel_id`, however, due to the derserialization
- // order there we can't make use of `ReadableArgs` to hand it in directly. Instead we opt
- // to initialize it with 0s and override it after reading the respective field via
- // `OnchainTxHandler::set_channel_id`.
+ // `ChannelMonitor`s already track the `channel_id` and `counterparty_node_id`, however, due
+ // to the deserialization order there we can't make use of `ReadableArgs` to hand them in
+ // directly. Instead we opt to initialize them with dummy values and override them after
+ // reading the respective fields via `OnchainTxHandler::set_channel_id` and
+ // `OnchainTxHandler::set_counterparty_node_id`.
let channel_id = ChannelId([0u8; 32]);
+ let counterparty_node_id = PublicKey::from_slice(&[2; 33]).unwrap();
let mut secp_ctx = Secp256k1::new();
secp_ctx.seeded_randomize(&entropy_source.get_secure_random_bytes());
Ok(OnchainTxHandler {
channel_id,
+ counterparty_node_id,
channel_value_satoshis,
channel_keys_id,
destination_script,
@@ -463,13 +476,14 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
pub(crate) fn new(
- channel_id: ChannelId, channel_value_satoshis: u64, channel_keys_id: [u8; 32],
- destination_script: ScriptBuf, signer: ChannelSigner,
+ channel_id: ChannelId, counterparty_node_id: PublicKey, channel_value_satoshis: u64,
+ channel_keys_id: [u8; 32], destination_script: ScriptBuf, signer: ChannelSigner,
channel_parameters: ChannelTransactionParameters,
holder_commitment: HolderCommitmentTransaction, secp_ctx: Secp256k1<secp256k1::All>,
) -> Self {
OnchainTxHandler {
channel_id,
+ counterparty_node_id,
channel_value_satoshis,
channel_keys_id,
destination_script,
@@ -533,7 +547,7 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
if tx.is_fully_signed() {
let log_start = if feerate_was_bumped { "Broadcasting RBF-bumped" } else { "Rebroadcasting" };
log_info!(logger, "{} onchain {}", log_start, log_tx!(tx.0));
- broadcaster.broadcast_transactions(&[(&tx.0, TransactionType::Claim { channel_id: self.channel_id })]);
+ broadcaster.broadcast_transactions(&[(&tx.0, TransactionType::Claim { counterparty_node_id: self.counterparty_node_id, channel_id: self.channel_id })]);
} else {
log_info!(logger, "Waiting for signature of unsigned onchain transaction {}", tx.0.compute_txid());
}
@@ -875,7 +889,7 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
OnchainClaim::Tx(tx) => {
if tx.is_fully_signed() {
log_info!(logger, "Broadcasting onchain {}", log_tx!(tx.0));
- broadcaster.broadcast_transactions(&[(&tx.0, TransactionType::Claim { channel_id: self.channel_id })]);
+ broadcaster.broadcast_transactions(&[(&tx.0, TransactionType::Claim { counterparty_node_id: self.counterparty_node_id, channel_id: self.channel_id })]);
} else {
log_info!(logger, "Waiting for signature of unsigned onchain transaction {}", tx.0.compute_txid());
}
@@ -1093,7 +1107,7 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
OnchainClaim::Tx(bump_tx) => {
if bump_tx.is_fully_signed() {
log_info!(logger, "Broadcasting RBF-bumped onchain {}", log_tx!(bump_tx.0));
- broadcaster.broadcast_transactions(&[(&bump_tx.0, TransactionType::Claim { channel_id: self.channel_id })]);
+ broadcaster.broadcast_transactions(&[(&bump_tx.0, TransactionType::Claim { counterparty_node_id: self.counterparty_node_id, channel_id: self.channel_id })]);
} else {
log_info!(logger, "Waiting for signature of RBF-bumped unsigned onchain transaction {}",
bump_tx.0.compute_txid());
@@ -1190,7 +1204,7 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
OnchainClaim::Tx(bump_tx) => {
if bump_tx.is_fully_signed() {
log_info!(logger, "Broadcasting onchain {}", log_tx!(bump_tx.0));
- broadcaster.broadcast_transactions(&[(&bump_tx.0, TransactionType::Claim { channel_id: self.channel_id })]);
+ broadcaster.broadcast_transactions(&[(&bump_tx.0, TransactionType::Claim { counterparty_node_id: self.counterparty_node_id, channel_id: self.channel_id })]);
} else {
log_info!(logger, "Waiting for signature of unsigned onchain transaction {}", bump_tx.0.compute_txid());
}
@@ -1368,8 +1382,10 @@ mod tests {
}
let holder_commit = HolderCommitmentTransaction::dummy(1000000, funding_outpoint, nondust_htlcs);
let destination_script = ScriptBuf::new();
+ let counterparty_node_id = PublicKey::from_slice(&[2; 33]).unwrap();
let mut tx_handler = OnchainTxHandler::new(
ChannelId::from_bytes([0; 32]),
+ counterparty_node_id,
1000000,
[0; 32],
destination_script.clone(),
diff --git a/lightning/src/events/bump_transaction/mod.rs b/lightning/src/events/bump_transaction/mod.rs
index 15ea277..ff03417 100644
--- a/lightning/src/events/bump_transaction/mod.rs
+++ b/lightning/src/events/bump_transaction/mod.rs
@@ -774,7 +774,7 @@ where
/// transaction spending an anchor output of the commitment transaction to bump its fee and
/// broadcasts them to the network as a package.
async fn handle_channel_close(
- &self, channel_id: ChannelId, claim_id: ClaimId,
+ &self, channel_id: ChannelId, counterparty_node_id: PublicKey, claim_id: ClaimId,
package_target_feerate_sat_per_1000_weight: u32, commitment_tx: &Transaction,
commitment_tx_fee_sat: u64, anchor_descriptor: &AnchorDescriptor,
) -> Result<(), ()> {
@@ -799,7 +799,7 @@ where
package_target_feerate_sat_per_1000_weight);
self.broadcaster.broadcast_transactions(&[(
&commitment_tx,
- TransactionType::UnilateralClose { channel_id },
+ TransactionType::UnilateralClose { counterparty_node_id, channel_id },
)]);
return Ok(());
}
@@ -968,8 +968,11 @@ where
commitment_tx.compute_txid()
);
self.broadcaster.broadcast_transactions(&[
- (&commitment_tx, TransactionType::UnilateralClose { channel_id }),
- (&anchor_tx, TransactionType::AnchorBump { channel_id }),
+ (
+ &commitment_tx,
+ TransactionType::UnilateralClose { counterparty_node_id, channel_id },
+ ),
+ (&anchor_tx, TransactionType::AnchorBump { counterparty_node_id, channel_id }),
]);
return Ok(());
}
@@ -978,8 +981,9 @@ where
/// Handles a [`BumpTransactionEvent::HTLCResolution`] event variant by producing a
/// fully-signed, fee-bumped HTLC transaction that is broadcast to the network.
async fn handle_htlc_resolution(
- &self, channel_id: ChannelId, claim_id: ClaimId, target_feerate_sat_per_1000_weight: u32,
- htlc_descriptors: &[HTLCDescriptor], tx_lock_time: LockTime,
+ &self, channel_id: ChannelId, counterparty_node_id: PublicKey, claim_id: ClaimId,
+ target_feerate_sat_per_1000_weight: u32, htlc_descriptors: &[HTLCDescriptor],
+ tx_lock_time: LockTime,
) -> Result<(), ()> {
let channel_type = &htlc_descriptors[0]
.channel_derivation_parameters
@@ -1205,7 +1209,7 @@ where
log_info!(self.logger, "Broadcasting {}", log_tx!(htlc_tx));
self.broadcaster.broadcast_transactions(&[(
&htlc_tx,
- TransactionType::UnilateralClose { channel_id },
+ TransactionType::UnilateralClose { counterparty_node_id, channel_id },
)]);
}
@@ -1217,6 +1221,7 @@ where
match event {
BumpTransactionEvent::ChannelClose {
channel_id,
+ counterparty_node_id,
claim_id,
package_target_feerate_sat_per_1000_weight,
commitment_tx,
@@ -1232,6 +1237,7 @@ where
);
self.handle_channel_close(
*channel_id,
+ *counterparty_node_id,
*claim_id,
*package_target_feerate_sat_per_1000_weight,
commitment_tx,
@@ -1249,6 +1255,7 @@ where
},
BumpTransactionEvent::HTLCResolution {
channel_id,
+ counterparty_node_id,
claim_id,
target_feerate_sat_per_1000_weight,
htlc_descriptors,
@@ -1263,6 +1270,7 @@ where
);
self.handle_htlc_resolution(
*channel_id,
+ *counterparty_node_id,
*claim_id,
*target_feerate_sat_per_1000_weight,
htlc_descriptors,
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 2762ab6..eb227a5 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -2110,6 +2110,7 @@ where
};
let channel_id = context.channel_id;
+ let counterparty_node_id = context.counterparty_node_id;
let signing_session = if let Some(signing_session) =
context.interactive_tx_signing_session.as_mut()
@@ -2223,9 +2224,9 @@ where
let funding_tx = funding_tx.map(|tx| {
let tx_type = if splice_negotiated.is_some() {
- TransactionType::Splice { channel_id }
+ TransactionType::Splice { counterparty_node_id, channel_id }
} else {
- TransactionType::Funding { channel_ids: vec![channel_id] }
+ TransactionType::Funding { channels: vec![(counterparty_node_id, channel_id)] }
};
(tx, tx_type)
});
@@ -9168,9 +9169,14 @@ where
let funding_tx = funding_tx.map(|tx| {
let tx_type = if splice_negotiated.is_some() {
- TransactionType::Splice { channel_id: self.context.channel_id }
+ TransactionType::Splice {
+ counterparty_node_id: self.context.counterparty_node_id,
+ channel_id: self.context.channel_id,
+ }
} else {
- TransactionType::Funding { channel_ids: vec![self.context.channel_id] }
+ TransactionType::Funding {
+ channels: vec![(self.context.counterparty_node_id, self.context.channel_id)],
+ }
};
(tx, tx_type)
});
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 665a79a..8b4a25c 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -6572,7 +6572,10 @@ impl<
funding_tx.compute_txid()
);
let tx_type = transaction_type.unwrap_or_else(|| TransactionType::Funding {
- channel_ids: vec![channel.context().channel_id()],
+ channels: vec![(
+ channel.context().get_counterparty_node_id(),
+ channel.context().channel_id(),
+ )],
});
self.tx_broadcaster.broadcast_transactions(&[(funding_tx, tx_type)]);
{
@@ -9583,7 +9586,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
let removed_batch_state = funding_batch_states.remove(&txid).into_iter().flatten();
let per_peer_state = self.per_peer_state.read().unwrap();
let mut batch_funding_tx = None;
- let mut batch_channel_ids = Vec::new();
+ let mut batch_channels = Vec::new();
for (channel_id, counterparty_node_id, _) in removed_batch_state {
if let Some(peer_state_mutex) = per_peer_state.get(&counterparty_node_id) {
let mut peer_state = peer_state_mutex.lock().unwrap();
@@ -9594,7 +9597,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
funded_chan.context.unbroadcasted_funding(&funded_chan.funding)
});
funded_chan.set_batch_ready();
- batch_channel_ids.push(channel_id);
+ batch_channels.push((counterparty_node_id, channel_id));
let mut pending_events = self.pending_events.lock().unwrap();
emit_channel_pending_event!(pending_events, funded_chan);
@@ -9605,7 +9608,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
log_info!(self.logger, "Broadcasting batch funding tx {}", tx.compute_txid());
self.tx_broadcaster.broadcast_transactions(&[(
&tx,
- TransactionType::Funding { channel_ids: batch_channel_ids },
+ TransactionType::Funding { channels: batch_channels },
)]);
}
}
@@ -10273,7 +10276,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
log_info!(logger, "Broadcasting funding transaction with txid {}", tx.compute_txid());
self.tx_broadcaster.broadcast_transactions(&[(
&tx,
- TransactionType::Funding { channel_ids: vec![channel.context.channel_id()] },
+ TransactionType::Funding { channels: vec![(counterparty_node_id, channel.context.channel_id())] },
)]);
}
}
@@ -11715,7 +11718,10 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
log_info!(logger, "Broadcasting {}", log_tx!(broadcast_tx));
self.tx_broadcaster.broadcast_transactions(&[(
&broadcast_tx,
- TransactionType::CooperativeClose { channel_id: msg.channel_id },
+ TransactionType::CooperativeClose {
+ counterparty_node_id: *counterparty_node_id,
+ channel_id: msg.channel_id,
+ },
)]);
let _ = self.handle_error(err, *counterparty_node_id);
}
@@ -12956,7 +12962,10 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
log_info!(logger, "Broadcasting closing tx {}", log_tx!(broadcast_tx));
self.tx_broadcaster.broadcast_transactions(&[(
&broadcast_tx,
- TransactionType::CooperativeClose { channel_id },
+ TransactionType::CooperativeClose {
+ counterparty_node_id: node_id,
+ channel_id,
+ },
)]);
}
} else {
@@ -13087,7 +13096,10 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
log_info!(logger, "Broadcasting {}", log_tx!(tx));
self.tx_broadcaster.broadcast_transactions(&[(
&tx,
- TransactionType::CooperativeClose { channel_id },
+ TransactionType::CooperativeClose {
+ counterparty_node_id: *cp_id,
+ channel_id,
+ },
)]);
false
} else {
diff --git a/lightning/src/ln/splicing_tests.rs b/lightning/src/ln/splicing_tests.rs
index 9c7c8b5..1ad7fa1 100644
--- a/lightning/src/ln/splicing_tests.rs
+++ b/lightning/src/ln/splicing_tests.rs
@@ -334,14 +334,22 @@ pub fn sign_interactive_funding_tx<'a, 'b, 'c, 'd>(
let tx = {
let mut initiator_txn = initiator.tx_broadcaster.txn_broadcast_with_types();
assert_eq!(initiator_txn.len(), 1);
- let acceptor_txn = acceptor.tx_broadcaster.txn_broadcast_with_types();
- assert_eq!(initiator_txn, acceptor_txn);
- let (tx, tx_type) = initiator_txn.remove(0);
- // Verify transaction type is Splice
+ let mut acceptor_txn = acceptor.tx_broadcaster.txn_broadcast_with_types();
+ assert_eq!(acceptor_txn.len(), 1);
+ // Compare transactions only (not types, as counterparty_node_id differs per perspective)
+ assert_eq!(initiator_txn[0].0, acceptor_txn[0].0);
+ let (tx, initiator_tx_type) = initiator_txn.remove(0);
+ let (_, acceptor_tx_type) = acceptor_txn.remove(0);
+ // Verify transaction types are Splice for both nodes
assert!(
- matches!(tx_type, TransactionType::Splice { .. }),
+ matches!(initiator_tx_type, TransactionType::Splice { .. }),
"Expected TransactionType::Splice, got {:?}",
- tx_type
+ initiator_tx_type
+ );
+ assert!(
+ matches!(acceptor_tx_type, TransactionType::Splice { .. }),
+ "Expected TransactionType::Splice, got {:?}",
+ acceptor_tx_type
);
tx
};
diff --git a/lightning/src/util/sweep.rs b/lightning/src/util/sweep.rs
index b0e3b0b..b70eb27 100644
--- a/lightning/src/util/sweep.rs
+++ b/lightning/src/util/sweep.rs
@@ -539,7 +539,7 @@ where
// Sweep the outputs.
let spending_tx_and_chan_id = self
.update_state(
- |sweeper_state| -> Result<(Option<(Transaction, Vec<ChannelId>)>, bool), ()> {
+ |sweeper_state| -> Result<(Option<(Transaction, Vec<(PublicKey, ChannelId)>)>, bool), ()> {
let cur_height = sweeper_state.best_block.height;
let cur_hash = sweeper_state.best_block.block_hash;
@@ -581,16 +581,20 @@ where
.outputs
.iter_mut()
.filter(|o| filter_fn(&**o, cur_height));
- let mut channel_ids = Vec::new();
+ let mut channels = Vec::new();
for output_info in respend_outputs {
if let Some(filter) = self.chain_data_source.as_ref() {
let watched_output = output_info.to_watched_output(cur_hash);
filter.register_output(watched_output);
}
- if let Some(channel_id) = output_info.channel_id {
- if !channel_ids.contains(&channel_id) {
- channel_ids.push(channel_id);
+ if let (Some(counterparty_node_id), Some(channel_id)) =
+ (output_info.counterparty_node_id, output_info.channel_id)
+ {
+ if !channels.iter().any(|(cp, ch)| {
+ *cp == counterparty_node_id && *ch == channel_id
+ }) {
+ channels.push((counterparty_node_id, channel_id));
}
}
@@ -598,7 +602,7 @@ where
sweeper_state.dirty = true;
}
- Ok((Some((spending_tx, channel_ids)), false))
+ Ok((Some((spending_tx, channels)), false))
} else {
Ok((None, false))
}
@@ -607,9 +611,9 @@ where
.await?;
// Persistence completely successfully. If we have a spending transaction, we broadcast it.
- if let Some((spending_tx, channel_ids)) = spending_tx_and_chan_id {
+ if let Some((spending_tx, channels)) = spending_tx_and_chan_id {
self.broadcaster
- .broadcast_transactions(&[(&spending_tx, TransactionType::Sweep { channel_ids })]);
+ .broadcast_transactions(&[(&spending_tx, TransactionType::Sweep { channels })]);
}
Ok(())
Why this scored 20/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.