Set version 3 on transactions for HTLC signatures in 0FC channels
What changed, and why it matters
This commit changes how certain Bitcoin transactions are versioned inside a specific type of Lightning channel (zero-fee-commitment, or 0FC, channels). For those channels, HTLC-related transactions now use transaction version 3 instead of version 2. Transaction version 3 is a newer standard that helps with network-wide fee management (transaction pinning resistance). The change only affects the transaction version number used when building and signing these transactions; it does not by itself move or expose funds.
Treat as a routine protocol-alignment patch. Review whether any consensus, mempool, or counterparty compatibility assumptions depend on version 2 for these transactions, and verify that version 3 is accepted by downstream signers and broadcast paths. No emergency action is indicated by the supplied materials.
Security signals we found
Transaction version changed from fixed v2 to conditional v3 for 0FC anchor channels
Change is narrowly scoped to HTLC transaction construction
No explicit security claim in commit message or diff
No CVE, advisory, or researcher attribution present in supplied materials
Evidence from the diff
In lightning/src/ln/chan_utils.rs, build_htlc_transaction now selects Version::non_standard(3) when channel_type_features.supports_anchor_zero_fee_commitments() is true, otherwise it keeps Version::TWO. This aligns HTLC second-stage transactions with BIP-431-style version 3 transactions for 0FC/anchor channels. The rest of the function is unchanged except for formatting. The commit message and diff do not describe a vulnerability or a specific exploit; they describe a protocol-versioning fix.
Changed components
lightning/src/ln/chan_utils.rsbuild_htlc_transactionHTLC second-stage transactions in zero-fee-commitment anchor channelsInspect captured patch +22 / −10
diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs
index be724a2..85bb51e 100644
--- a/lightning/src/ln/chan_utils.rs
+++ b/lightning/src/ln/chan_utils.rs
@@ -810,18 +810,30 @@ pub(crate) fn make_funding_redeemscript_from_slices(broadcaster_funding_key: &[u
///
/// Panics if htlc.transaction_output_index.is_none() (as such HTLCs do not appear in the
/// commitment transaction).
-#[rustfmt::skip]
-pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, contest_delay: u16, htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, broadcaster_delayed_payment_key: &DelayedPaymentKey, revocation_key: &RevocationKey) -> Transaction {
- let txins= vec![build_htlc_input(commitment_txid, htlc, channel_type_features)];
-
- let mut txouts: Vec<TxOut> = Vec::new();
- txouts.push(build_htlc_output(
- feerate_per_kw, contest_delay, htlc, channel_type_features,
- broadcaster_delayed_payment_key, revocation_key
- ));
+pub fn build_htlc_transaction(
+ commitment_txid: &Txid, feerate_per_kw: u32, contest_delay: u16, htlc: &HTLCOutputInCommitment,
+ channel_type_features: &ChannelTypeFeatures,
+ broadcaster_delayed_payment_key: &DelayedPaymentKey, revocation_key: &RevocationKey,
+) -> Transaction {
+ let txins = vec![build_htlc_input(commitment_txid, htlc, channel_type_features)];
+
+ let txouts: Vec<TxOut> = vec![build_htlc_output(
+ feerate_per_kw,
+ contest_delay,
+ htlc,
+ channel_type_features,
+ broadcaster_delayed_payment_key,
+ revocation_key,
+ )];
+
+ let version = if channel_type_features.supports_anchor_zero_fee_commitments() {
+ Version::non_standard(3)
+ } else {
+ Version::TWO
+ };
Transaction {
- version: Version::TWO,
+ version,
lock_time: LockTime::from_consensus(if htlc.offered { htlc.cltv_expiry } else { 0 }),
input: txins,
output: txouts,
Why this scored 42/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.