-
Notifications
You must be signed in to change notification settings - Fork 417
Description
Recently, I encountered a bug that an HTLC is not claimed by either party after the channel is force-closed.
Logs indicate that one node is attempting to claim that HTLC via an htlc-timeout transaction, but failed with RPC error (400) due to dust output.
Here is part of the logs. cf3..0f4:11
is the HTLC to be swept.
2025-06-05T08:02:40.100395Z DEBUG lightning::chain::package:1062: Adding claiming input for outpoint cf3f0c985e6c130f2a3988e888d58d8cc51945efaceb7bfec70cd95ca086d0f4:11
2025-06-05T08:02:40.100719Z INFO lightning::chain::onchaintx:528: Rebroadcasting onchain HTLC claim tx (0 preimage, 1 timeout, 0 revoked) with txid 2ade336033804fe0fd619a49f7c9dd50c455cc4cbceb8118252d90158de30cca
2025-06-05T08:02:40.101883Z DEBUG ldk_node::chain:1036: Failed to broadcast due to HTTP connection error: RPC error
tx: 02000000000101f4d086a05cd90cc7fe7bebacef4519c58c8dd588e888392a0f136c5e980c3fcf0b00000000010000000200000000000000001600141dec8ff322b271c6fd27d855c2afeeacf868d9e50000000000000000226a2088aa90deac782500cbeb1787dccc9e941ea865a6511dba98f53bac3fa81135bd0347304402200a424a7942f5f7887d6e1a2a8c17430a601ac9f015c3ee974e68dffee5d07677022075b6a389adeaa661ab0ff86b21fd8027f47b5421e6beaa2cea54da913a4a95ef01008d76a914a64d88bc4137d38b7cd5bdd77806f859cacb9bfb8763ac67210299164eef8e0b0fdde0556aa98143dfd7f2ff6fd18728b01b188041b4766d279c7c8201208763a914c797839ad0401e45b721c50a695fb9d241de668a88527c2103646ebe4f487aee44dc9e585301899699cf53645aff81095522da45d7921329f052ae677502db01b175ac6851b27568b6030000
2025-06-05T08:03:09.172879Z DEBUG lightning_transaction_sync::esplora:248: Finished transaction sync at tip 0e3441163f7c09ed394e421e014ce1fb04af05f74b969d7f9c00ea71f5d0d54c in 0ms: 0 confirmed, 0 unconfirmed.
2025-06-05T08:03:09.172909Z INFO ldk_node::chain:592: Sync of Lightning wallet finished in 0ms.
2025-06-05T08:03:10.238034Z INFO lightning::chain::onchaintx:513: Triggering rebroadcast/fee-bump for request with inputs [OutPoint { txid: cf3f0c985e6c130f2a3988e888d58d8cc51945efaceb7bfec70cd95ca086d0f4, vout: 11 }]
2025-06-05T08:03:10.238073Z DEBUG lightning::chain::package:1341: Initiating fee rate bump from 1570 s/kWU (1133 s) to 253 s/kWU (182 s) using HighestOfPreviousOrNew strategy
2025-06-05T08:03:10.238076Z DEBUG lightning::chain::package:1372: new feerate 1570 is equal to previous feerate 1570
2025-06-05T08:03:10.238078Z DEBUG lightning::chain::package:1373: new fee: 1133, input amount: 1000
2025-06-05T08:03:10.238078Z DEBUG lightning::chain::package:1374: remaining output amount is 0
I guess the dust output occurred because the remote party claimed some of its inputs and the node removed them from the sweep transaction without adjusting the feerate.
The sweep transaction before its output becomes dust (ignore the OP_RETURN commitment):
tx: Transaction {
version: Version(
2,
),
lock_time: 940 blocks,
input: [
TxIn {
previous_output: OutPoint {
txid: cf3f0c985e6c130f2a3988e888d58d8cc51945efaceb7bfec70cd95ca086d0f4,
vout: 8,
},
script_sig: Script(),
sequence: Sequence(0x00000001),
witness: Witness: {
indices: 0,
indices_start: 0,
witnesses: [
],
}
,
},
TxIn {
previous_output: OutPoint {
txid: cf3f0c985e6c130f2a3988e888d58d8cc51945efaceb7bfec70cd95ca086d0f4,
vout: 11,
},
script_sig: Script(),
sequence: Sequence(0x00000001),
witness: Witness: {
indices: 0,
indices_start: 0,
witnesses: [
],
}
,
},
TxIn {
previous_output: OutPoint {
txid: cf3f0c985e6c130f2a3988e888d58d8cc51945efaceb7bfec70cd95ca086d0f4,
vout: 10,
},
script_sig: Script(),
sequence: Sequence(0x00000001),
witness: Witness: {
indices: 0,
indices_start: 0,
witnesses: [
],
}
,
},
],
output: [
TxOut {
value: 661 SAT,
script_pubkey: Script(OP_0 OP_PUSHBYTES_20 1dec8ff322b271c6fd27d855c2afeeacf868d9e5),
},
TxOut {
value: 0 SAT,
script_pubkey: Script(OP_RETURN OP_PUSHBYTES_32 0143f6b131ea8f65166dcfbe8ad9255068bd7c7459e6418d9bd0901293fca52f),
},
],
},
HTLCs at vout 8 and 10 were claimed by the remote party
2025-06-03T05:19:25.062327Z DEBUG lightning::chain::onchaintx:1036: Removing claim tracking due to maturation of claim tx for outpoints:
2025-06-03T05:19:25.062328Z DEBUG lightning::chain::onchaintx:1037: [OutPoint { txid: cf3f0c985e6c130f2a3988e888d58d8cc51945efaceb7bfec70cd95ca086d0f4, vout: 10 }]
2025-06-03T05:19:25.062331Z DEBUG lightning::chain::onchaintx:1036: Removing claim tracking due to maturation of claim tx for outpoints:
2025-06-03T05:19:25.062332Z DEBUG lightning::chain::onchaintx:1037: [OutPoint { txid: cf3f0c985e6c130f2a3988e888d58d8cc51945efaceb7bfec70cd95ca086d0f4, vout: 8 }]
2025-06-03T05:19:25.062336Z DEBUG lightning::chain::package:1341: Initiating fee rate bump from 1570 s/kWU (1133 s) to 253 s/kWU (182 s) using ForceBump strategy
2025-06-03T05:19:25.062339Z WARN lightning::chain::package:1388: Can't bump new claiming tx, output amount 0 would end up below dust threshold 294
Because the feerate is unchanged during the following feerate_bump
, it bypasses the dust check.
rust-lightning/lightning/src/chain/package.rs
Lines 1570 to 1590 in 97f0e4b
debug_assert!(new_feerate >= previous_feerate); | |
if new_feerate == previous_feerate { | |
return Some((new_fee, new_feerate)); | |
} | |
let min_relay_fee = INCREMENTAL_RELAY_FEE_SAT_PER_1000_WEIGHT * predicted_weight / 1000; | |
// BIP 125 Opt-in Full Replace-by-Fee Signaling | |
// * 3. The replacement transaction pays an absolute fee of at least the sum paid by the original transactions. | |
// * 4. The replacement transaction must also pay for its own bandwidth at or above the rate set by the node's minimum relay fee setting. | |
let naive_new_fee = new_fee; | |
let new_fee = cmp::max(new_fee, previous_fee + min_relay_fee); | |
if new_fee > naive_new_fee { | |
log_debug!(logger, "Naive fee bump of {}s does not meet min relay fee requirements of {}s", naive_new_fee - previous_fee, min_relay_fee); | |
} | |
let remaining_output_amount = input_amounts.saturating_sub(new_fee); | |
if remaining_output_amount < dust_limit_sats { | |
log_warn!(logger, "Can't bump new claiming tx, output amount {} would end up below dust threshold {}", remaining_output_amount, dust_limit_sats); | |
return None; | |
} |
rust-lightning/lightning/src/chain/package.rs
Lines 1363 to 1374 in 97f0e4b
if self.feerate_previous != 0 { | |
if let Some((new_fee, feerate)) = feerate_bump( | |
predicted_weight, input_amounts, dust_limit_sats, self.feerate_previous, | |
feerate_strategy, conf_target, fee_estimator, logger, | |
) { | |
return Some((input_amounts.saturating_sub(new_fee), feerate)); | |
} | |
} else { | |
if let Some((new_fee, feerate)) = compute_fee_from_spent_amounts(input_amounts, predicted_weight, conf_target, fee_estimator, logger) { | |
return Some((cmp::max(input_amounts as i64 - new_fee as i64, dust_limit_sats as i64) as u64, feerate)); | |
} | |
} |
The HTLC sweep transaction with dust output:
{
"result": {
"txid": "2ade336033804fe0fd619a49f7c9dd50c455cc4cbceb8118252d90158de30cca",
"hash": "140c48a6b29aedc093955e43308f7078602ffdcdcbeaf9924740ca35e88d0d5b",
"version": 2,
"size": 343,
"vsize": 180,
"weight": 718,
"locktime": 950,
"vin": [
{
"txid": "cf3f0c985e6c130f2a3988e888d58d8cc51945efaceb7bfec70cd95ca086d0f4",
"vout": 11,
"scriptSig": {
"asm": "",
"hex": ""
},
"txinwitness": [
"304402200a424a7942f5f7887d6e1a2a8c17430a601ac9f015c3ee974e68dffee5d07677022075b6a389adeaa661ab0ff86b21fd8027f47b5421e6beaa2cea54da913a4a95ef01",
"",
"76a914a64d88bc4137d38b7cd5bdd77806f859cacb9bfb8763ac67210299164eef8e0b0fdde0556aa98143dfd7f2ff6fd18728b01b188041b4766d279c7c8201208763a914c797839ad0401e45b721c50a695fb9d241de668a88527c2103646ebe4f487aee44dc9e585301899699cf53645aff81095522da45d7921329f052ae677502db01b175ac6851b27568"
],
"sequence": 1
}
],
"vout": [
{
"value": "0.00000000",
"n": 0,
"scriptPubKey": {
"asm": "0 1dec8ff322b271c6fd27d855c2afeeacf868d9e5",
"desc": "addr(bc1qrhkgluezkfcudlf8mp2u9tlw4nux3k09jlxq4d)#p3wysca3",
"hex": "00141dec8ff322b271c6fd27d855c2afeeacf868d9e5",
"address": "bc1qrhkgluezkfcudlf8mp2u9tlw4nux3k09jlxq4d",
"type": "witness_v0_keyhash"
}
},
{
"value": "0.00000000",
"n": 1,
"scriptPubKey": {
"asm": "OP_RETURN 88aa90deac782500cbeb1787dccc9e941ea865a6511dba98f53bac3fa81135bd",
"desc": "raw(6a2088aa90deac782500cbeb1787dccc9e941ea865a6511dba98f53bac3fa81135bd)#pap99d6q",
"hex": "6a2088aa90deac782500cbeb1787dccc9e941ea865a6511dba98f53bac3fa81135bd",
"type": "nulldata"
}
}
]
},
"error": null
}