Check dust exposure on receiving `commitment_signed`, not `update_fee`
What changed, and why it matters
This commit changes when a Lightning node checks whether a proposed fee change would create risky 'dust' exposure. Previously, the check happened immediately when the peer sent an `update_fee` message. Now, the check is deferred until the peer sends `commitment_signed`, which is the moment the new fee would actually take effect. This allows a peer to send a fee update that temporarily looks risky, as long as they add or remove HTLCs before signing the commitment so that the final state is safe. The change also removes some duplicated fee-calculation code and moves an affordability check into the same deferred validation path.
Review the new `validate_commitment_signed` path to confirm that all pending fee updates are validated exactly once and that the dust-exposure and reserve checks cover both local and remote commitment views. Ensure test coverage includes cases where `update_fee` alone would exceed dust limits but `commitment_signed` does not, and vice versa.
Security signals we found
Validation timing moved from `update_fee` to `commitment_signed`
Dust-exposure limit now evaluated against final commitment state rather than intermediate fee message
Counterparty fee affordability checked at commitment time, not update-fee time
Removal of duplicated commitment-stats computation reduces risk of inconsistent validation
Channel force-close is the enforcement mechanism for violations
Evidence from the diff
The patch moves dust-exposure and fee-affordability validation from update_fee handling into validate_commitment_signed. Channel::update_fee now only records the pending feerate and no longer calls validate_update_fee. When a remote commitment_signed arrives, validate_commitment_signed invokes validate_update_fee if a remote-announced fee update is pending. validate_update_fee now takes a raw new_feerate_per_kw instead of the full UpdateFee message and additionally verifies that the counterparty can afford the new fee after reserving the holder-selected channel reserve. The commit removes the CommitmentStats field from CommitmentData, deletes the build_commitment_stats helper and per-HTLC is_dust methods, and inlines the remaining assertions into build_commitment_transaction. Test call sites are updated to pass a LowerBoundedFeeEstimator to commitment_signed.
Changed components
lightning/src/ln/channel.rslightning/src/ln/channelmanager.rslightning/src/ln/chanmon_update_fail_tests.rslightning/src/ln/functional_tests.rsInspect captured patch +87 / −185
diff --git a/lightning/src/ln/chanmon_update_fail_tests.rs b/lightning/src/ln/chanmon_update_fail_tests.rs
index 469bab7..1bc1bfb 100644
--- a/lightning/src/ln/chanmon_update_fail_tests.rs
+++ b/lightning/src/ln/chanmon_update_fail_tests.rs
@@ -12,6 +12,7 @@
//! There are a bunch of these as their handling is relatively error-prone so they are split out
//! here. See also the chanmon_fail_consistency fuzz test.
+use crate::chain::chaininterface::LowerBoundedFeeEstimator;
use crate::chain::chainmonitor::ChainMonitor;
use crate::chain::channelmonitor::{ChannelMonitor, MonitorEvent, ANTI_REORG_DELAY};
use crate::chain::transaction::OutPoint;
@@ -133,9 +134,12 @@ fn test_monitor_and_persister_update_fail() {
let chan_opt = get_channel_ref!(nodes[0], nodes[1], per_peer_lock, peer_state_lock, chan.2);
if let Some(channel) = chan_opt.as_funded_mut() {
assert_eq!(updates.commitment_signed.len(), 1);
- if let Ok(Some(update)) =
- channel.commitment_signed(&updates.commitment_signed[0], &node_cfgs[0].logger)
- {
+ let feeest = LowerBoundedFeeEstimator::new(&chanmon_cfgs[0].fee_estimator);
+ if let Ok(Some(update)) = channel.commitment_signed(
+ &updates.commitment_signed[0],
+ &feeest,
+ &node_cfgs[0].logger,
+ ) {
// Check that the persister returns InProgress (and will never actually complete)
// as the monitor update errors.
if let ChannelMonitorUpdateStatus::InProgress =
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 3c7a825..ffa25b6 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -303,24 +303,6 @@ struct InboundHTLCOutput {
state: InboundHTLCState,
}
-impl InboundHTLCOutput {
- fn is_dust(
- &self, local: bool, feerate_per_kw: u32, broadcaster_dust_limit_sat: u64,
- features: &ChannelTypeFeatures,
- ) -> bool {
- let (htlc_success_tx_fee_sat, htlc_timeout_tx_fee_sat) =
- second_stage_tx_fees_sat(features, feerate_per_kw);
-
- let htlc_tx_fee_sat = if !local {
- // This is an offered HTLC.
- htlc_timeout_tx_fee_sat
- } else {
- htlc_success_tx_fee_sat
- };
- self.amount_msat / 1000 < broadcaster_dust_limit_sat + htlc_tx_fee_sat
- }
-}
-
#[cfg_attr(test, derive(Clone, Debug, PartialEq))]
enum OutboundHTLCState {
/// Added by us and included in a commitment_signed (if we were AwaitingRemoteRevoke when we
@@ -447,24 +429,6 @@ struct OutboundHTLCOutput {
hold_htlc: Option<()>,
}
-impl OutboundHTLCOutput {
- fn is_dust(
- &self, local: bool, feerate_per_kw: u32, broadcaster_dust_limit_sat: u64,
- features: &ChannelTypeFeatures,
- ) -> bool {
- let (htlc_success_tx_fee_sat, htlc_timeout_tx_fee_sat) =
- second_stage_tx_fees_sat(features, feerate_per_kw);
-
- let htlc_tx_fee_sat = if local {
- // This is an offered HTLC.
- htlc_timeout_tx_fee_sat
- } else {
- htlc_success_tx_fee_sat
- };
- self.amount_msat / 1000 < broadcaster_dust_limit_sat + htlc_tx_fee_sat
- }
-}
-
/// See AwaitingRemoteRevoke ChannelState for more info
#[cfg_attr(test, derive(Clone, Debug, PartialEq))]
enum HTLCUpdateAwaitingACK {
@@ -1101,7 +1065,6 @@ struct HTLCStats {
/// A struct gathering data on a commitment, either local or remote.
struct CommitmentData<'a> {
tx: CommitmentTransaction,
- stats: CommitmentStats,
htlcs_included: Vec<(HTLCOutputInCommitment, Option<&'a HTLCSource>)>, // the list of HTLCs (dust HTLCs *included*) which were not ignored when building the transaction
outbound_htlc_preimages: Vec<PaymentPreimage>, // preimages for successful offered HTLCs since last commitment
inbound_htlc_preimages: Vec<PaymentPreimage>, // preimages for successful received HTLCs since last commitment
@@ -2005,10 +1968,11 @@ where
}
#[rustfmt::skip]
- pub fn commitment_signed<L: Deref>(
- &mut self, msg: &msgs::CommitmentSigned, best_block: BestBlock, signer_provider: &SP, logger: &L
+ pub fn commitment_signed<F: Deref, L: Deref>(
+ &mut self, msg: &msgs::CommitmentSigned, best_block: BestBlock, signer_provider: &SP, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L
) -> Result<(Option<ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>>, Option<ChannelMonitorUpdate>), ChannelError>
where
+ F::Target: FeeEstimator,
L::Target: Logger
{
let phase = core::mem::replace(&mut self.phase, ChannelPhase::Undefined);
@@ -2060,10 +2024,10 @@ where
.unwrap_or(true);
let res = if has_negotiated_pending_splice && !session_received_commitment_signed {
funded_channel
- .splice_initial_commitment_signed(msg, logger)
+ .splice_initial_commitment_signed(msg, fee_estimator, logger)
.map(|monitor_update_opt| (None, monitor_update_opt))
} else {
- funded_channel.commitment_signed(msg, logger)
+ funded_channel.commitment_signed(msg, fee_estimator, logger)
.map(|monitor_update_opt| (None, monitor_update_opt))
};
@@ -4715,7 +4679,7 @@ where
fn validate_update_fee<F: Deref>(
&self, funding: &FundingScope, fee_estimator: &LowerBoundedFeeEstimator<F>,
- msg: &msgs::UpdateFee,
+ new_feerate_per_kw: u32,
) -> Result<(), ChannelError>
where
F::Target: FeeEstimator,
@@ -4732,7 +4696,7 @@ where
None,
include_counterparty_unknown_htlcs,
0,
- msg.feerate_per_kw,
+ new_feerate_per_kw,
dust_exposure_limiting_feerate,
)
.map_err(|()| {
@@ -4740,13 +4704,25 @@ where
"Balance after HTLCs and anchors exhausted on local commitment",
))
})?;
+
+ next_local_commitment_stats
+ .get_holder_counterparty_balances_incl_fee_msat()
+ .and_then(|(_, counterparty_balance_incl_fee_msat)| {
+ counterparty_balance_incl_fee_msat
+ .checked_sub(funding.holder_selected_channel_reserve_satoshis * 1000)
+ .ok_or(())
+ })
+ .map_err(|()| {
+ ChannelError::close("Funding remote cannot afford proposed new fee".to_owned())
+ })?;
+
let next_remote_commitment_stats = self
.get_next_remote_commitment_stats(
funding,
None,
include_counterparty_unknown_htlcs,
0,
- msg.feerate_per_kw,
+ new_feerate_per_kw,
dust_exposure_limiting_feerate,
)
.map_err(|()| {
@@ -4761,7 +4737,7 @@ where
return Err(ChannelError::close(
format!(
"Peer sent update_fee with a feerate ({}) which may over-expose us to dust-in-flight on our own transactions (totaling {} msat)",
- msg.feerate_per_kw,
+ new_feerate_per_kw,
next_local_commitment_stats.dust_exposure_msat,
)
));
@@ -4770,7 +4746,7 @@ where
return Err(ChannelError::close(
format!(
"Peer sent update_fee with a feerate ({}) which may over-expose us to dust-in-flight on our counterparty's transactions (totaling {} msat)",
- msg.feerate_per_kw,
+ new_feerate_per_kw,
next_remote_commitment_stats.dust_exposure_msat,
)
));
@@ -4779,14 +4755,15 @@ where
Ok(())
}
- fn validate_commitment_signed<L: Deref>(
+ fn validate_commitment_signed<F: Deref, L: Deref>(
&self, funding: &FundingScope, transaction_number: u64, commitment_point: PublicKey,
- msg: &msgs::CommitmentSigned, logger: &L,
+ msg: &msgs::CommitmentSigned, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
) -> Result<
(HolderCommitmentTransaction, Vec<(HTLCOutputInCommitment, Option<&HTLCSource>)>),
ChannelError,
>
where
+ F::Target: FeeEstimator,
L::Target: Logger,
{
let funding_script = funding.get_funding_redeemscript();
@@ -4825,36 +4802,10 @@ where
// If our counterparty updated the channel fee in this commitment transaction, check that
// they can actually afford the new fee now.
- let update_fee = if let Some((_, update_state)) = self.pending_update_fee {
- update_state == FeeUpdateState::RemoteAnnounced
- } else {
- false
- };
- if update_fee {
- debug_assert!(!funding.is_outbound());
- let counterparty_reserve_we_require_msat =
- funding.holder_selected_channel_reserve_satoshis * 1000;
- if commitment_data.stats.remote_balance_before_fee_msat
- < commitment_data.stats.commit_tx_fee_sat * 1000
- + counterparty_reserve_we_require_msat
- {
- return Err(ChannelError::close(
- "Funding remote cannot afford proposed new fee".to_owned(),
- ));
- }
- }
- #[cfg(any(test, fuzzing))]
+ if let Some((new_feerate_per_kw, FeeUpdateState::RemoteAnnounced)) = self.pending_update_fee
{
- let PredictedNextFee {
- predicted_feerate,
- predicted_nondust_htlc_count,
- predicted_fee_sat,
- } = *funding.next_local_fee.lock().unwrap();
- if predicted_feerate == commitment_data.tx.negotiated_feerate_per_kw()
- && predicted_nondust_htlc_count == commitment_data.tx.nondust_htlcs().len()
- {
- assert_eq!(predicted_fee_sat, commitment_data.stats.commit_tx_fee_sat);
- }
+ debug_assert!(!funding.is_outbound());
+ self.validate_update_fee(funding, fee_estimator, new_feerate_per_kw)?;
}
if msg.htlc_signatures.len() != commitment_data.tx.nondust_htlcs().len() {
@@ -5134,83 +5085,6 @@ where
feerate_per_kw
}
- /// Builds stats on a potential commitment transaction build, without actually building the
- /// commitment transaction. See `build_commitment_transaction` for further docs.
- #[inline]
- #[rustfmt::skip]
- fn build_commitment_stats(&self, funding: &FundingScope, local: bool, generated_by_local: bool, feerate_per_kw: Option<u32>, fee_buffer_nondust_htlcs: Option<usize>) -> CommitmentStats {
- let broadcaster_dust_limit_sat = if local { self.holder_dust_limit_satoshis } else { self.counterparty_dust_limit_satoshis };
- let mut nondust_htlc_count = 0;
- let mut remote_htlc_total_msat = 0;
- let mut local_htlc_total_msat = 0;
- let mut value_to_self_claimed_msat = 0;
- let mut value_to_remote_claimed_msat = 0;
-
- let feerate_per_kw = feerate_per_kw.unwrap_or_else(|| self.get_commitment_feerate(funding, generated_by_local));
-
- for htlc in self.pending_inbound_htlcs.iter() {
- if htlc.state.included_in_commitment(generated_by_local) {
- if !htlc.is_dust(local, feerate_per_kw, broadcaster_dust_limit_sat, funding.get_channel_type()) {
- nondust_htlc_count += 1;
- }
- remote_htlc_total_msat += htlc.amount_msat;
- } else {
- if htlc.state.preimage().is_some() {
- value_to_self_claimed_msat += htlc.amount_msat;
- }
- }
- };
-
- for htlc in self.pending_outbound_htlcs.iter() {
- if htlc.state.included_in_commitment(generated_by_local) {
- if !htlc.is_dust(local, feerate_per_kw, broadcaster_dust_limit_sat, funding.get_channel_type()) {
- nondust_htlc_count += 1;
- }
- local_htlc_total_msat += htlc.amount_msat;
- } else {
- if htlc.state.preimage().is_some() {
- value_to_remote_claimed_msat += htlc.amount_msat;
- }
- }
- };
-
- // # Panics
- //
- // After all HTLC claims have been accounted for, the local balance MUST remain greater than or equal to 0.
-
- let mut value_to_self_msat = (funding.value_to_self_msat + value_to_self_claimed_msat).checked_sub(value_to_remote_claimed_msat).unwrap();
-
- let mut value_to_remote_msat = (funding.get_value_satoshis() * 1000).checked_sub(value_to_self_msat).unwrap();
- value_to_self_msat = value_to_self_msat.checked_sub(local_htlc_total_msat).unwrap();
- value_to_remote_msat = value_to_remote_msat.checked_sub(remote_htlc_total_msat).unwrap();
-
- #[cfg(debug_assertions)]
- {
- // Make sure that the to_self/to_remote is always either past the appropriate
- // channel_reserve *or* it is making progress towards it.
- let mut broadcaster_max_commitment_tx_output = if generated_by_local {
- funding.holder_max_commitment_tx_output.lock().unwrap()
- } else {
- funding.counterparty_max_commitment_tx_output.lock().unwrap()
- };
- debug_assert!(broadcaster_max_commitment_tx_output.0 <= value_to_self_msat || value_to_self_msat / 1000 >= funding.counterparty_selected_channel_reserve_satoshis.unwrap());
- broadcaster_max_commitment_tx_output.0 = cmp::max(broadcaster_max_commitment_tx_output.0, value_to_self_msat);
- debug_assert!(broadcaster_max_commitment_tx_output.1 <= value_to_remote_msat || value_to_remote_msat / 1000 >= funding.holder_selected_channel_reserve_satoshis);
- broadcaster_max_commitment_tx_output.1 = cmp::max(broadcaster_max_commitment_tx_output.1, value_to_remote_msat);
- }
-
- let commit_tx_fee_sat = SpecTxBuilder {}.commit_tx_fee_sat(feerate_per_kw, nondust_htlc_count + fee_buffer_nondust_htlcs.unwrap_or(0), funding.get_channel_type());
- // Subtract any non-HTLC outputs from the local and remote balances
- let (local_balance_before_fee_msat, remote_balance_before_fee_msat) = SpecTxBuilder {}.subtract_non_htlc_outputs(
- funding.is_outbound(),
- value_to_self_msat,
- value_to_remote_msat,
- funding.get_channel_type(),
- );
-
- CommitmentStats { commit_tx_fee_sat, local_balance_before_fee_msat, remote_balance_before_fee_msat }
- }
-
/// Transaction nomenclature is somewhat confusing here as there are many different cases - a
/// transaction is referred to as "a's transaction" implying that a will be able to broadcast
/// the transaction. Thus, b will generally be sending a signature over such a transaction to
@@ -5311,7 +5185,28 @@ where
broadcaster_dust_limit_sat,
logger,
);
- debug_assert_eq!(stats, self.build_commitment_stats(funding, local, generated_by_local, None, None), "Caught an inconsistency between `TxBuilder::build_commitment_transaction` and the rest of the `TxBuilder` methods");
+ #[cfg(any(test, fuzzing))]
+ {
+ let PredictedNextFee { predicted_feerate, predicted_nondust_htlc_count, predicted_fee_sat } = if local { *funding.next_local_fee.lock().unwrap() } else { *funding.next_remote_fee.lock().unwrap() };
+ if predicted_feerate == tx.negotiated_feerate_per_kw() && predicted_nondust_htlc_count == tx.nondust_htlcs().len() {
+ assert_eq!(predicted_fee_sat, stats.commit_tx_fee_sat);
+ }
+ }
+ #[cfg(debug_assertions)]
+ {
+ // Make sure that the to_self/to_remote is always either past the appropriate
+ // channel_reserve *or* it is making progress towards it.
+ let mut broadcaster_max_commitment_tx_output = if generated_by_local {
+ funding.holder_max_commitment_tx_output.lock().unwrap()
+ } else {
+ funding.counterparty_max_commitment_tx_output.lock().unwrap()
+ };
+ debug_assert!(broadcaster_max_commitment_tx_output.0 <= stats.local_balance_before_fee_msat || stats.local_balance_before_fee_msat / 1000 >= funding.counterparty_selected_channel_reserve_satoshis.unwrap());
+ broadcaster_max_commitment_tx_output.0 = cmp::max(broadcaster_max_commitment_tx_output.0, stats.local_balance_before_fee_msat);
+ debug_assert!(broadcaster_max_commitment_tx_output.1 <= stats.remote_balance_before_fee_msat || stats.remote_balance_before_fee_msat / 1000 >= funding.holder_selected_channel_reserve_satoshis);
+ broadcaster_max_commitment_tx_output.1 = cmp::max(broadcaster_max_commitment_tx_output.1, stats.remote_balance_before_fee_msat);
+ }
+
// This populates the HTLC-source table with the indices from the HTLCs in the commitment
// transaction.
@@ -5346,7 +5241,6 @@ where
CommitmentData {
tx,
- stats,
htlcs_included,
inbound_htlc_preimages,
outbound_htlc_preimages,
@@ -7641,10 +7535,12 @@ where
/// Note that our `commitment_signed` send did not include a monitor update. This is due to:
/// 1. Updates cannot be made since the state machine is paused until `tx_signatures`.
/// 2. We're still able to abort negotiation until `tx_signatures`.
- fn splice_initial_commitment_signed<L: Deref>(
- &mut self, msg: &msgs::CommitmentSigned, logger: &L,
+ fn splice_initial_commitment_signed<F: Deref, L: Deref>(
+ &mut self, msg: &msgs::CommitmentSigned, fee_estimator: &LowerBoundedFeeEstimator<F>,
+ logger: &L,
) -> Result<Option<ChannelMonitorUpdate>, ChannelError>
where
+ F::Target: FeeEstimator,
L::Target: Logger,
{
debug_assert!(self
@@ -7676,6 +7572,7 @@ where
transaction_number,
commitment_point,
msg,
+ fee_estimator,
logger,
)?;
// This corresponds to the same `commitment_signed` we sent earlier, which we know to be the
@@ -7745,10 +7642,12 @@ where
(nondust_htlc_sources, dust_htlcs)
}
- pub fn commitment_signed<L: Deref>(
- &mut self, msg: &msgs::CommitmentSigned, logger: &L,
+ pub fn commitment_signed<F: Deref, L: Deref>(
+ &mut self, msg: &msgs::CommitmentSigned, fee_estimator: &LowerBoundedFeeEstimator<F>,
+ logger: &L,
) -> Result<Option<ChannelMonitorUpdate>, ChannelError>
where
+ F::Target: FeeEstimator,
L::Target: Logger,
{
self.commitment_signed_check_state()?;
@@ -7768,6 +7667,7 @@ where
transaction_number,
commitment_point,
msg,
+ fee_estimator,
logger,
)
.map(|(commitment_tx, htlcs_included)| {
@@ -7786,10 +7686,12 @@ where
self.commitment_signed_update_monitor(update, logger)
}
- pub fn commitment_signed_batch<L: Deref>(
- &mut self, batch: Vec<msgs::CommitmentSigned>, logger: &L,
+ pub fn commitment_signed_batch<F: Deref, L: Deref>(
+ &mut self, batch: Vec<msgs::CommitmentSigned>, fee_estimator: &LowerBoundedFeeEstimator<F>,
+ logger: &L,
) -> Result<Option<ChannelMonitorUpdate>, ChannelError>
where
+ F::Target: FeeEstimator,
L::Target: Logger,
{
self.commitment_signed_check_state()?;
@@ -7838,6 +7740,7 @@ where
transaction_number,
commitment_point,
msg,
+ fee_estimator,
logger,
)?;
commitment_txs.push(commitment_tx);
@@ -9163,10 +9066,7 @@ where
self.context.pending_update_fee = Some((msg.feerate_per_kw, FeeUpdateState::RemoteAnnounced));
self.context.update_time_counter += 1;
-
- core::iter::once(&self.funding)
- .chain(self.pending_funding().iter())
- .try_for_each(|funding| self.context.validate_update_fee(funding, fee_estimator, msg))
+ Ok(())
}
/// Indicates that the signer may have some signatures for us, so we should retry if we're
@@ -12378,14 +12278,6 @@ where
);
let counterparty_commitment_tx = commitment_data.tx;
- #[cfg(any(test, fuzzing))]
- {
- let PredictedNextFee { predicted_feerate, predicted_nondust_htlc_count, predicted_fee_sat } = *funding.next_remote_fee.lock().unwrap();
- if predicted_feerate == counterparty_commitment_tx.negotiated_feerate_per_kw() && predicted_nondust_htlc_count == counterparty_commitment_tx.nondust_htlcs().len() {
- assert_eq!(predicted_fee_sat, commitment_data.stats.commit_tx_fee_sat);
- }
- }
-
(commitment_data.htlcs_included, counterparty_commitment_tx)
}
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 0bdca77..c771f9a 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -10783,7 +10783,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
let logger = WithChannelContext::from(&self.logger, &chan.context(), None);
let funding_txo = chan.funding().get_funding_txo();
let (monitor_opt, monitor_update_opt) = try_channel_entry!(
- self, peer_state, chan.commitment_signed(msg, best_block, &self.signer_provider, &&logger),
+ self, peer_state, chan.commitment_signed(msg, best_block, &self.signer_provider, &self.fee_estimator, &&logger),
chan_entry);
if let Some(chan) = chan.as_funded_mut() {
@@ -10828,7 +10828,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
let funding_txo = chan.funding().get_funding_txo();
if let Some(chan) = chan.as_funded_mut() {
let monitor_update_opt = try_channel_entry!(
- self, peer_state, chan.commitment_signed_batch(batch, &&logger), chan_entry
+ self, peer_state, chan.commitment_signed_batch(batch, &self.fee_estimator, &&logger), chan_entry
);
if let Some(monitor_update) = monitor_update_opt {
diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs
index b961da2..aabce4a 100644
--- a/lightning/src/ln/functional_tests.rs
+++ b/lightning/src/ln/functional_tests.rs
@@ -7281,9 +7281,12 @@ pub fn test_update_err_monitor_lockdown() {
get_channel_ref!(nodes[0], nodes[1], per_peer_lock, peer_state_lock, chan_1.2);
if let Some(channel) = chan_ref.as_funded_mut() {
assert_eq!(updates.commitment_signed.len(), 1);
- if let Ok(Some(update)) =
- channel.commitment_signed(&updates.commitment_signed[0], &node_cfgs[0].logger)
- {
+ let feeest = LowerBoundedFeeEstimator::new(&chanmon_cfgs[0].fee_estimator);
+ if let Ok(Some(update)) = channel.commitment_signed(
+ &updates.commitment_signed[0],
+ &feeest,
+ &node_cfgs[0].logger,
+ ) {
assert_eq!(
watchtower.chain_monitor.update_channel(chan_1.2, &update),
ChannelMonitorUpdateStatus::InProgress
@@ -7434,9 +7437,12 @@ pub fn test_concurrent_monitor_claim() {
get_channel_ref!(nodes[0], nodes[1], per_peer_lock, peer_state_lock, chan_1.2);
if let Some(channel) = chan_ref.as_funded_mut() {
assert_eq!(updates.commitment_signed.len(), 1);
- if let Ok(Some(update)) =
- channel.commitment_signed(&updates.commitment_signed[0], &node_cfgs[0].logger)
- {
+ let feeest = LowerBoundedFeeEstimator::new(&chanmon_cfgs[0].fee_estimator);
+ if let Ok(Some(update)) = channel.commitment_signed(
+ &updates.commitment_signed[0],
+ &feeest,
+ &node_cfgs[0].logger,
+ ) {
// Watchtower Alice should already have seen the block and reject the update
assert_eq!(
watchtower_alice.chain_monitor.update_channel(chan_1.2, &update),
Why this scored 63/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.