Skip to content

Commit 8a1c9d9

Browse files
committed
Add validation of the fees predicted by next_commitment_stats
Anytime we build a (feerate, nondust-htlc-count, fee) pair, cache it, and check that the fee matches if the feerate and nondust-htlc-count match when building a commitment transaction.
1 parent 124bd42 commit 8a1c9d9

File tree

1 file changed

+114
-5
lines changed

1 file changed

+114
-5
lines changed

lightning/src/ln/channel.rs

Lines changed: 114 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2062,6 +2062,10 @@ pub(super) struct FundingScope {
20622062
next_local_commitment_tx_fee_info_cached: Mutex<Option<CommitmentTxInfoCached>>,
20632063
#[cfg(any(test, fuzzing))]
20642064
next_remote_commitment_tx_fee_info_cached: Mutex<Option<CommitmentTxInfoCached>>,
2065+
#[cfg(any(test, fuzzing))]
2066+
next_local_fee: Mutex<PredictedNextFee>,
2067+
#[cfg(any(test, fuzzing))]
2068+
next_remote_fee: Mutex<PredictedNextFee>,
20652069

20662070
pub(super) channel_transaction_parameters: ChannelTransactionParameters,
20672071

@@ -2138,6 +2142,10 @@ impl Readable for FundingScope {
21382142
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
21392143
#[cfg(any(test, fuzzing))]
21402144
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
2145+
#[cfg(any(test, fuzzing))]
2146+
next_local_fee: Mutex::new(PredictedNextFee::default()),
2147+
#[cfg(any(test, fuzzing))]
2148+
next_remote_fee: Mutex::new(PredictedNextFee::default()),
21412149
})
21422150
}
21432151
}
@@ -2314,6 +2322,10 @@ impl FundingScope {
23142322
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
23152323
#[cfg(any(test, fuzzing))]
23162324
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
2325+
#[cfg(any(test, fuzzing))]
2326+
next_local_fee: Mutex::new(PredictedNextFee::default()),
2327+
#[cfg(any(test, fuzzing))]
2328+
next_remote_fee: Mutex::new(PredictedNextFee::default()),
23172329
funding_tx_confirmation_height: 0,
23182330
funding_tx_confirmed_in: None,
23192331
minimum_depth_override: None,
@@ -3197,6 +3209,10 @@ where
31973209
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
31983210
#[cfg(any(test, fuzzing))]
31993211
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
3212+
#[cfg(any(test, fuzzing))]
3213+
next_local_fee: Mutex::new(PredictedNextFee::default()),
3214+
#[cfg(any(test, fuzzing))]
3215+
next_remote_fee: Mutex::new(PredictedNextFee::default()),
32003216

32013217
channel_transaction_parameters: ChannelTransactionParameters {
32023218
holder_pubkeys: pubkeys,
@@ -3437,6 +3453,10 @@ where
34373453
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
34383454
#[cfg(any(test, fuzzing))]
34393455
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
3456+
#[cfg(any(test, fuzzing))]
3457+
next_local_fee: Mutex::new(PredictedNextFee::default()),
3458+
#[cfg(any(test, fuzzing))]
3459+
next_remote_fee: Mutex::new(PredictedNextFee::default()),
34403460

34413461
channel_transaction_parameters: ChannelTransactionParameters {
34423462
holder_pubkeys: pubkeys,
@@ -4216,7 +4236,8 @@ where
42164236
include_counterparty_unknown_htlcs,
42174237
);
42184238
let next_value_to_self_msat = self.get_next_commitment_value_to_self_msat(true, funding);
4219-
SpecTxBuilder {}.get_next_commitment_stats(
4239+
4240+
let ret = SpecTxBuilder {}.get_next_commitment_stats(
42204241
true,
42214242
funding.is_outbound(),
42224243
funding.get_value_satoshis(),
@@ -4227,7 +4248,38 @@ where
42274248
dust_exposure_limiting_feerate,
42284249
self.holder_dust_limit_satoshis,
42294250
funding.get_channel_type(),
4230-
)
4251+
);
4252+
4253+
#[cfg(any(test, fuzzing))]
4254+
{
4255+
if addl_nondust_htlc_count == 0 {
4256+
*funding.next_local_fee.lock().unwrap() = PredictedNextFee {
4257+
predicted_feerate: feerate_per_kw,
4258+
predicted_nondust_htlc_count: ret.nondust_htlc_count,
4259+
predicted_fee_sat: ret.commit_tx_fee_sat,
4260+
};
4261+
} else {
4262+
let predicted_stats = SpecTxBuilder {}.get_next_commitment_stats(
4263+
true,
4264+
funding.is_outbound(),
4265+
funding.get_value_satoshis(),
4266+
next_value_to_self_msat,
4267+
&next_commitment_htlcs,
4268+
0,
4269+
feerate_per_kw,
4270+
dust_exposure_limiting_feerate,
4271+
self.holder_dust_limit_satoshis,
4272+
funding.get_channel_type(),
4273+
);
4274+
*funding.next_local_fee.lock().unwrap() = PredictedNextFee {
4275+
predicted_feerate: feerate_per_kw,
4276+
predicted_nondust_htlc_count: predicted_stats.nondust_htlc_count,
4277+
predicted_fee_sat: predicted_stats.commit_tx_fee_sat,
4278+
};
4279+
}
4280+
}
4281+
4282+
ret
42314283
}
42324284

42334285
fn get_next_remote_commitment_stats(
@@ -4241,7 +4293,8 @@ where
42414293
include_counterparty_unknown_htlcs,
42424294
);
42434295
let next_value_to_self_msat = self.get_next_commitment_value_to_self_msat(false, funding);
4244-
SpecTxBuilder {}.get_next_commitment_stats(
4296+
4297+
let ret = SpecTxBuilder {}.get_next_commitment_stats(
42454298
false,
42464299
funding.is_outbound(),
42474300
funding.get_value_satoshis(),
@@ -4252,7 +4305,38 @@ where
42524305
dust_exposure_limiting_feerate,
42534306
self.counterparty_dust_limit_satoshis,
42544307
funding.get_channel_type(),
4255-
)
4308+
);
4309+
4310+
#[cfg(any(test, fuzzing))]
4311+
{
4312+
if addl_nondust_htlc_count == 0 {
4313+
*funding.next_remote_fee.lock().unwrap() = PredictedNextFee {
4314+
predicted_feerate: feerate_per_kw,
4315+
predicted_nondust_htlc_count: ret.nondust_htlc_count,
4316+
predicted_fee_sat: ret.commit_tx_fee_sat,
4317+
};
4318+
} else {
4319+
let predicted_stats = SpecTxBuilder {}.get_next_commitment_stats(
4320+
false,
4321+
funding.is_outbound(),
4322+
funding.get_value_satoshis(),
4323+
next_value_to_self_msat,
4324+
&next_commitment_htlcs,
4325+
0,
4326+
feerate_per_kw,
4327+
dust_exposure_limiting_feerate,
4328+
self.counterparty_dust_limit_satoshis,
4329+
funding.get_channel_type(),
4330+
);
4331+
*funding.next_remote_fee.lock().unwrap() = PredictedNextFee {
4332+
predicted_feerate: feerate_per_kw,
4333+
predicted_nondust_htlc_count: predicted_stats.nondust_htlc_count,
4334+
predicted_fee_sat: predicted_stats.commit_tx_fee_sat,
4335+
};
4336+
}
4337+
}
4338+
4339+
ret
42564340
}
42574341

42584342
#[rustfmt::skip]
@@ -4410,6 +4494,10 @@ where
44104494
}
44114495
}
44124496
}
4497+
let PredictedNextFee { predicted_feerate, predicted_nondust_htlc_count, predicted_fee_sat } = *funding.next_local_fee.lock().unwrap();
4498+
if predicted_feerate == commitment_data.tx.feerate_per_kw() && predicted_nondust_htlc_count == commitment_data.tx.nondust_htlcs().len() {
4499+
assert_eq!(predicted_fee_sat, commitment_data.stats.commit_tx_fee_sat);
4500+
}
44134501
}
44144502

44154503
if msg.htlc_signatures.len() != commitment_data.tx.nondust_htlcs().len() {
@@ -6216,6 +6304,14 @@ struct CommitmentTxInfoCached {
62166304
feerate: u32,
62176305
}
62186306

6307+
#[cfg(any(test, fuzzing))]
6308+
#[derive(Clone, Copy, Default)]
6309+
struct PredictedNextFee {
6310+
predicted_feerate: u32,
6311+
predicted_nondust_htlc_count: usize,
6312+
predicted_fee_sat: u64,
6313+
}
6314+
62196315
/// Contents of a wire message that fails an HTLC backwards. Useful for [`FundedChannel::fail_htlc`] to
62206316
/// fail with either [`msgs::UpdateFailMalformedHTLC`] or [`msgs::UpdateFailHTLC`] as needed.
62216317
trait FailHTLCContents {
@@ -11386,6 +11482,10 @@ where
1138611482
}
1138711483
}
1138811484
}
11485+
let PredictedNextFee { predicted_feerate, predicted_nondust_htlc_count, predicted_fee_sat } = *funding.next_remote_fee.lock().unwrap();
11486+
if predicted_feerate == counterparty_commitment_tx.feerate_per_kw() && predicted_nondust_htlc_count == counterparty_commitment_tx.nondust_htlcs().len() {
11487+
assert_eq!(predicted_fee_sat, commitment_data.stats.commit_tx_fee_sat);
11488+
}
1138911489
}
1139011490

1139111491
(commitment_data.htlcs_included, counterparty_commitment_tx)
@@ -14032,6 +14132,10 @@ where
1403214132
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
1403314133
#[cfg(any(test, fuzzing))]
1403414134
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
14135+
#[cfg(any(test, fuzzing))]
14136+
next_local_fee: Mutex::new(PredictedNextFee::default()),
14137+
#[cfg(any(test, fuzzing))]
14138+
next_remote_fee: Mutex::new(PredictedNextFee::default()),
1403514139

1403614140
channel_transaction_parameters: channel_parameters,
1403714141
funding_transaction,
@@ -16092,7 +16196,7 @@ mod tests {
1609216196
fn get_pre_and_post(
1609316197
pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64,
1609416198
) -> (u64, u64) {
16095-
use crate::ln::channel::FundingScope;
16199+
use crate::ln::channel::{FundingScope, PredictedNextFee};
1609616200

1609716201
let funding = FundingScope {
1609816202
value_to_self_msat: 0,
@@ -16109,6 +16213,11 @@ mod tests {
1610916213
#[cfg(any(test, fuzzing))]
1611016214
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
1611116215

16216+
#[cfg(any(test, fuzzing))]
16217+
next_local_fee: Mutex::new(PredictedNextFee::default()),
16218+
#[cfg(any(test, fuzzing))]
16219+
next_remote_fee: Mutex::new(PredictedNextFee::default()),
16220+
1611216221
channel_transaction_parameters: ChannelTransactionParameters::test_dummy(
1611316222
pre_channel_value,
1611416223
),

0 commit comments

Comments
 (0)