Skip to content

Commit 9176956

Browse files
committed
Refactor BroadcasterInterface to include TransactionType
Add a `TransactionType` enum to provide context about the type of transaction being broadcast. This information can be useful for logging, filtering, or prioritization purposes. The `TransactionType` variants are: - `Funding`: A funding transaction establishing a new channel - `CooperativeClose`: A cooperative close transaction - `UnilateralClose`: A force-close transaction - `AnchorBump`: An anchor transaction for CPFP fee-bumping - `Claim`: A transaction claiming outputs from commitment tx - `Sweep`: A transaction sweeping spendable outputs to wallet Co-Authored-By: HAL 9000 Signed-off-by: Elias Rohrer <dev@tnull.de>
1 parent 9df0280 commit 9176956

11 files changed

Lines changed: 236 additions & 102 deletions

File tree

fuzz/src/chanmon_consistency.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ use bitcoin::WPubkeyHash;
3636
use lightning::blinded_path::message::{BlindedMessagePath, MessageContext, MessageForwardNode};
3737
use lightning::blinded_path::payment::{BlindedPaymentPath, ReceiveTlvs};
3838
use lightning::chain;
39-
use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
39+
use lightning::chain::chaininterface::{
40+
TransactionType, BroadcasterInterface, ConfirmationTarget, FeeEstimator,
41+
};
4042
use lightning::chain::channelmonitor::{ChannelMonitor, MonitorEvent};
4143
use lightning::chain::transaction::OutPoint;
4244
use lightning::chain::{
@@ -159,8 +161,8 @@ pub struct TestBroadcaster {
159161
txn_broadcasted: RefCell<Vec<Transaction>>,
160162
}
161163
impl BroadcasterInterface for TestBroadcaster {
162-
fn broadcast_transactions(&self, txs: &[&Transaction]) {
163-
for tx in txs {
164+
fn broadcast_transactions(&self, txs: &[(&Transaction, TransactionType)]) {
165+
for (tx, _broadcast_type) in txs {
164166
self.txn_broadcasted.borrow_mut().push((*tx).clone());
165167
}
166168
}

fuzz/src/full_stack.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ use bitcoin::WPubkeyHash;
3333
use lightning::blinded_path::message::{BlindedMessagePath, MessageContext, MessageForwardNode};
3434
use lightning::blinded_path::payment::{BlindedPaymentPath, ReceiveTlvs};
3535
use lightning::chain;
36-
use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
36+
use lightning::chain::chaininterface::{
37+
TransactionType, BroadcasterInterface, ConfirmationTarget, FeeEstimator,
38+
};
3739
use lightning::chain::chainmonitor;
3840
use lightning::chain::transaction::OutPoint;
3941
use lightning::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen};
@@ -184,8 +186,8 @@ struct TestBroadcaster {
184186
txn_broadcasted: Mutex<Vec<Transaction>>,
185187
}
186188
impl BroadcasterInterface for TestBroadcaster {
187-
fn broadcast_transactions(&self, txs: &[&Transaction]) {
188-
let owned_txs: Vec<Transaction> = txs.iter().map(|tx| (*tx).clone()).collect();
189+
fn broadcast_transactions(&self, txs: &[(&Transaction, TransactionType)]) {
190+
let owned_txs: Vec<Transaction> = txs.iter().map(|(tx, _)| (*tx).clone()).collect();
189191
self.txn_broadcasted.lock().unwrap().extend(owned_txs);
190192
}
191193
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[build]
2+
rustflags = ["--cfg=lsps1_service"]

lightning-liquidity/src/lsps2/service.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use crate::prelude::{new_hash_map, HashMap};
4040
use crate::sync::{Arc, Mutex, MutexGuard, RwLock};
4141
use crate::utils::async_poll::dummy_waker;
4242

43-
use lightning::chain::chaininterface::BroadcasterInterface;
43+
use lightning::chain::chaininterface::{BroadcasterInterface, TransactionType};
4444
use lightning::events::HTLCHandlingFailureType;
4545
use lightning::ln::channelmanager::{AChannelManager, FailureCode, InterceptId};
4646
use lightning::ln::msgs::{ErrorAction, LightningError};
@@ -2023,23 +2023,24 @@ where
20232023
// (for example when a forwarded HTLC nears expiry). Broadcasting funding after a
20242024
// close could then confirm the commitment and trigger unintended on‑chain handling.
20252025
// To avoid this, we check ChannelManager’s view (`is_channel_ready`) before broadcasting.
2026-
let channel_id_opt = jit_channel.get_channel_id();
2027-
if let Some(ch_id) = channel_id_opt {
2026+
if let Some(ch_id) = jit_channel.get_channel_id() {
20282027
let is_channel_ready = self
20292028
.channel_manager
20302029
.get_cm()
20312030
.list_channels()
20322031
.into_iter()
20332032
.any(|cd| cd.channel_id == ch_id && cd.is_channel_ready);
2033+
20342034
if !is_channel_ready {
20352035
return;
20362036
}
2037-
} else {
2038-
return;
2039-
}
20402037

2041-
if let Some(funding_tx) = jit_channel.get_funding_tx() {
2042-
self.tx_broadcaster.broadcast_transactions(&[funding_tx]);
2038+
if let Some(funding_tx) = jit_channel.get_funding_tx() {
2039+
self.tx_broadcaster.broadcast_transactions(&[(
2040+
funding_tx,
2041+
TransactionType::Funding { channel_ids: vec![ch_id] },
2042+
)]);
2043+
}
20432044
}
20442045
}
20452046
}

lightning/src/chain/chaininterface.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,56 @@
1515
1616
use core::{cmp, ops::Deref};
1717

18+
use crate::ln::types::ChannelId;
1819
use crate::prelude::*;
1920

2021
use bitcoin::transaction::Transaction;
2122

23+
/// Represents the class of transaction being broadcast.
24+
///
25+
/// This is used to provide context about the type of transaction being broadcast, which may be
26+
/// useful for logging, filtering, or prioritization purposes.
27+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
28+
pub enum TransactionType {
29+
/// A funding transaction establishing a new channel.
30+
Funding {
31+
/// The IDs of the channels being funded.
32+
///
33+
/// A single funding transaction may establish multiple channels when using batch funding.
34+
channel_ids: Vec<ChannelId>,
35+
},
36+
/// A transaction cooperatively closing a channel.
37+
CooperativeClose {
38+
/// The ID of the channel being closed.
39+
channel_id: ChannelId,
40+
},
41+
/// A transaction being broadcast to force-close the channel.
42+
UnilateralClose {
43+
/// The ID of the channel being force-closed.
44+
channel_id: ChannelId,
45+
},
46+
/// An anchor bumping transaction used for CPFP fee-bumping a closing transaction.
47+
AnchorBump {
48+
/// The ID of the channel whose closing transaction is being fee-bumped.
49+
channel_id: ChannelId,
50+
},
51+
/// A transaction claiming outputs from a commitment transaction (HTLC claims, penalty/justice).
52+
Claim {
53+
/// The ID of the channel from which outputs are being claimed.
54+
channel_id: ChannelId,
55+
},
56+
/// A transaction genered by the [`OutputSweeper`], sweeping [`SpendableOutputDescriptor`]s to the user's wallet.
57+
///
58+
/// [`OutputSweeper`]: crate::util::sweep::OutputSweeper
59+
/// [`SpendableOutputDescriptor`]: crate::sign::SpendableOutputDescriptor
60+
Sweep {
61+
/// The IDs of the channels from which outputs are being swept, if known.
62+
///
63+
/// A single sweep transaction may aggregate outputs from multiple channels.
64+
channel_ids: Vec<ChannelId>,
65+
},
66+
}
67+
2268
// TODO: Define typed abstraction over feerates to handle their conversions.
2369
pub(crate) fn compute_feerate_sat_per_1000_weight(fee_sat: u64, weight: u64) -> u32 {
2470
(fee_sat * 1000 / weight).try_into().unwrap_or(u32::max_value())
@@ -45,7 +91,10 @@ pub trait BroadcasterInterface {
4591
///
4692
/// Bitcoin transaction packages are defined in BIP 331 and here:
4793
/// <https://github.com/bitcoin/bitcoin/blob/master/doc/policy/packages.md>
48-
fn broadcast_transactions(&self, txs: &[&Transaction]);
94+
///
95+
/// Each transaction is paired with a [`TransactionType`] indicating the class of transaction
96+
/// being broadcast, which may be useful for logging, filtering, or prioritization.
97+
fn broadcast_transactions(&self, txs: &[(&Transaction, TransactionType)]);
4998
}
5099

51100
/// An enum that represents the priority at which we want a transaction to confirm used for feerate

lightning/src/chain/channelmonitor.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1886,8 +1886,9 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
18861886
initial_holder_commitment_tx.trust().commitment_number();
18871887

18881888
let onchain_tx_handler = OnchainTxHandler::new(
1889-
channel_parameters.channel_value_satoshis, channel_keys_id, destination_script.into(),
1890-
keys, channel_parameters.clone(), initial_holder_commitment_tx.clone(), secp_ctx
1889+
channel_id, channel_parameters.channel_value_satoshis, channel_keys_id,
1890+
destination_script.into(), keys, channel_parameters.clone(),
1891+
initial_holder_commitment_tx.clone(), secp_ctx,
18911892
);
18921893

18931894
let funding_outpoint = channel_parameters.funding_outpoint
@@ -6598,7 +6599,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
65986599
return Err(DecodeError::InvalidValue);
65996600
}
66006601
}
6601-
let onchain_tx_handler: OnchainTxHandler<SP::EcdsaSigner> = ReadableArgs::read(
6602+
let mut onchain_tx_handler: OnchainTxHandler<SP::EcdsaSigner> = ReadableArgs::read(
66026603
reader, (entropy_source, signer_provider, channel_value_satoshis, channel_keys_id)
66036604
)?;
66046605

@@ -6694,6 +6695,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
66946695
}
66956696

66966697
let channel_id = channel_id.unwrap_or(ChannelId::v1_from_funding_outpoint(outpoint));
6698+
onchain_tx_handler.set_channel_id(channel_id);
66976699

66986700
let (current_holder_commitment_tx, current_holder_htlc_data) = {
66996701
let holder_commitment_tx = onchain_tx_handler.current_holder_commitment_tx();

lightning/src/chain/onchaintx.rs

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ use bitcoin::transaction::OutPoint as BitcoinOutPoint;
2323
use bitcoin::transaction::Transaction;
2424

2525
use crate::chain::chaininterface::ConfirmationTarget;
26-
use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator, LowerBoundedFeeEstimator};
26+
use crate::chain::chaininterface::{
27+
BroadcasterInterface, FeeEstimator, LowerBoundedFeeEstimator, TransactionType,
28+
};
2729
use crate::chain::channelmonitor::ANTI_REORG_DELAY;
2830
use crate::chain::package::{PackageSolvingData, PackageTemplate};
2931
use crate::chain::transaction::MaybeSignedTransaction;
@@ -33,6 +35,7 @@ use crate::ln::chan_utils::{
3335
HTLCOutputInCommitment, HolderCommitmentTransaction,
3436
};
3537
use crate::ln::msgs::DecodeError;
38+
use crate::ln::types::ChannelId;
3639
use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, HTLCDescriptor, SignerProvider};
3740
use crate::util::logger::Logger;
3841
use crate::util::ser::{
@@ -221,6 +224,7 @@ pub(crate) enum FeerateStrategy {
221224
/// do RBF bumping if possible.
222225
#[derive(Clone)]
223226
pub struct OnchainTxHandler<ChannelSigner: EcdsaChannelSigner> {
227+
channel_id: ChannelId,
224228
channel_value_satoshis: u64, // Deprecated as of 0.2.
225229
channel_keys_id: [u8; 32], // Deprecated as of 0.2.
226230
destination_script: ScriptBuf, // Deprecated as of 0.2.
@@ -283,7 +287,8 @@ impl<ChannelSigner: EcdsaChannelSigner> PartialEq for OnchainTxHandler<ChannelSi
283287
#[rustfmt::skip]
284288
fn eq(&self, other: &Self) -> bool {
285289
// `signer`, `secp_ctx`, and `pending_claim_events` are excluded on purpose.
286-
self.channel_value_satoshis == other.channel_value_satoshis &&
290+
self.channel_id == other.channel_id &&
291+
self.channel_value_satoshis == other.channel_value_satoshis &&
287292
self.channel_keys_id == other.channel_keys_id &&
288293
self.destination_script == other.destination_script &&
289294
self.holder_commitment == other.holder_commitment &&
@@ -346,6 +351,14 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
346351
write_tlv_fields!(writer, {});
347352
Ok(())
348353
}
354+
355+
// `ChannelMonitor`s already track the `channel_id`, however, due to the derserialization order
356+
// there we can't make use of `ReadableArgs` to hand it into `OnchainTxHandler`'s
357+
// deserialization logic directly. Instead we opt to initialize it with 0s and override it
358+
// after reading the respective field via this method.
359+
pub(crate) fn set_channel_id(&mut self, channel_id: ChannelId) {
360+
self.channel_id = channel_id;
361+
}
349362
}
350363

351364
impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP, u64, [u8; 32])>
@@ -367,7 +380,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
367380
let prev_holder_commitment = Readable::read(reader)?;
368381
let _prev_holder_htlc_sigs: Option<Vec<Option<(usize, Signature)>>> = Readable::read(reader)?;
369382

370-
let channel_parameters = ReadableArgs::<Option<u64>>::read(reader, Some(channel_value_satoshis))?;
383+
let channel_parameters: ChannelTransactionParameters = ReadableArgs::<Option<u64>>::read(reader, Some(channel_value_satoshis))?;
371384

372385
// Read the serialized signer bytes, but don't deserialize them, as we'll obtain our signer
373386
// by re-deriving the private key material.
@@ -421,10 +434,17 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
421434

422435
read_tlv_fields!(reader, {});
423436

437+
// `ChannelMonitor`s already track the `channel_id`, however, due to the derserialization
438+
// order there we can't make use of `ReadableArgs` to hand it in directly. Instead we opt
439+
// to initialize it with 0s and override it after reading the respective field via
440+
// `OnchainTxHandler::set_channel_id`.
441+
let channel_id = ChannelId([0u8; 32]);
442+
424443
let mut secp_ctx = Secp256k1::new();
425444
secp_ctx.seeded_randomize(&entropy_source.get_secure_random_bytes());
426445

427446
Ok(OnchainTxHandler {
447+
channel_id,
428448
channel_value_satoshis,
429449
channel_keys_id,
430450
destination_script,
@@ -444,11 +464,13 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
444464

445465
impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
446466
pub(crate) fn new(
447-
channel_value_satoshis: u64, channel_keys_id: [u8; 32], destination_script: ScriptBuf,
448-
signer: ChannelSigner, channel_parameters: ChannelTransactionParameters,
467+
channel_id: ChannelId, channel_value_satoshis: u64, channel_keys_id: [u8; 32],
468+
destination_script: ScriptBuf, signer: ChannelSigner,
469+
channel_parameters: ChannelTransactionParameters,
449470
holder_commitment: HolderCommitmentTransaction, secp_ctx: Secp256k1<secp256k1::All>,
450471
) -> Self {
451472
OnchainTxHandler {
473+
channel_id,
452474
channel_value_satoshis,
453475
channel_keys_id,
454476
destination_script,
@@ -516,7 +538,7 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
516538
if tx.is_fully_signed() {
517539
let log_start = if feerate_was_bumped { "Broadcasting RBF-bumped" } else { "Rebroadcasting" };
518540
log_info!(logger, "{} onchain {}", log_start, log_tx!(tx.0));
519-
broadcaster.broadcast_transactions(&[&tx.0]);
541+
broadcaster.broadcast_transactions(&[(&tx.0, TransactionType::Claim { channel_id: self.channel_id })]);
520542
} else {
521543
log_info!(logger, "Waiting for signature of unsigned onchain transaction {}", tx.0.compute_txid());
522544
}
@@ -863,7 +885,7 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
863885
OnchainClaim::Tx(tx) => {
864886
if tx.is_fully_signed() {
865887
log_info!(logger, "Broadcasting onchain {}", log_tx!(tx.0));
866-
broadcaster.broadcast_transactions(&[&tx.0]);
888+
broadcaster.broadcast_transactions(&[(&tx.0, TransactionType::Claim { channel_id: self.channel_id })]);
867889
} else {
868890
log_info!(logger, "Waiting for signature of unsigned onchain transaction {}", tx.0.compute_txid());
869891
}
@@ -1084,7 +1106,7 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
10841106
OnchainClaim::Tx(bump_tx) => {
10851107
if bump_tx.is_fully_signed() {
10861108
log_info!(logger, "Broadcasting RBF-bumped onchain {}", log_tx!(bump_tx.0));
1087-
broadcaster.broadcast_transactions(&[&bump_tx.0]);
1109+
broadcaster.broadcast_transactions(&[(&bump_tx.0, TransactionType::Claim { channel_id: self.channel_id })]);
10881110
} else {
10891111
log_info!(logger, "Waiting for signature of RBF-bumped unsigned onchain transaction {}",
10901112
bump_tx.0.compute_txid());
@@ -1187,7 +1209,7 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
11871209
OnchainClaim::Tx(bump_tx) => {
11881210
if bump_tx.is_fully_signed() {
11891211
log_info!(logger, "Broadcasting onchain {}", log_tx!(bump_tx.0));
1190-
broadcaster.broadcast_transactions(&[&bump_tx.0]);
1212+
broadcaster.broadcast_transactions(&[(&bump_tx.0, TransactionType::Claim { channel_id: self.channel_id })]);
11911213
} else {
11921214
log_info!(logger, "Waiting for signature of unsigned onchain transaction {}", bump_tx.0.compute_txid());
11931215
}
@@ -1281,6 +1303,7 @@ mod tests {
12811303
};
12821304
use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint, RevocationBasepoint};
12831305
use crate::ln::functional_test_utils::create_dummy_block;
1306+
use crate::ln::types::ChannelId;
12841307
use crate::sign::{ChannelDerivationParameters, ChannelSigner, HTLCDescriptor, InMemorySigner};
12851308
use crate::types::payment::{PaymentHash, PaymentPreimage};
12861309
use crate::util::test_utils::{TestBroadcaster, TestFeeEstimator, TestLogger};
@@ -1365,6 +1388,7 @@ mod tests {
13651388
let holder_commit = HolderCommitmentTransaction::dummy(1000000, funding_outpoint, nondust_htlcs);
13661389
let destination_script = ScriptBuf::new();
13671390
let mut tx_handler = OnchainTxHandler::new(
1391+
ChannelId::from_bytes([0; 32]),
13681392
1000000,
13691393
[0; 32],
13701394
destination_script.clone(),

0 commit comments

Comments
 (0)