Skip to content

Commit cebe9f3

Browse files
committed
Remove i64 casts in ChannelContext::build_commitment_transaction
Instead of converting operands to `i64` and checking if the subtractions overflowed by checking if the `i64` is smaller than zero, we instead choose to do checked subtractions on the original unsigned integers. In addition, prior to this commit, we remained silent if the subtraction of the total fee and the anchors from the funder's balance underflowed; we now panic in this case.
1 parent 592f17d commit cebe9f3

File tree

1 file changed

+37
-27
lines changed

1 file changed

+37
-27
lines changed

lightning/src/ln/channel.rs

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3758,14 +3758,17 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
37583758
}
37593759
}
37603760

3761-
let value_to_self_msat: i64 = (funding.value_to_self_msat - local_htlc_total_msat) as i64 + value_to_self_msat_offset;
3762-
assert!(value_to_self_msat >= 0);
3763-
// Note that in case they have several just-awaiting-last-RAA fulfills in-progress (ie
3764-
// AwaitingRemoteRevokeToRemove or AwaitingRemovedRemoteRevoke) we may have allowed them to
3765-
// "violate" their reserve value by couting those against it. Thus, we have to convert
3766-
// everything to i64 before subtracting as otherwise we can overflow.
3767-
let value_to_remote_msat: i64 = (funding.get_value_satoshis() * 1000) as i64 - (funding.value_to_self_msat as i64) - (remote_htlc_total_msat as i64) - value_to_self_msat_offset;
3768-
assert!(value_to_remote_msat >= 0);
3761+
// # Panics
3762+
//
3763+
// While we expect `value_to_self_msat_offset` to be negative in some cases, the value going
3764+
// to each party MUST be 0 or positive, even if all HTLCs pending in the commitment clear by
3765+
// failure.
3766+
3767+
// TODO: When MSRV >= 1.66.0, use u64::checked_add_signed
3768+
let mut value_to_self_msat = (funding.value_to_self_msat as i64 + value_to_self_msat_offset).try_into().unwrap();
3769+
let mut value_to_remote_msat = (funding.get_value_satoshis() * 1000).checked_sub(value_to_self_msat).unwrap();
3770+
value_to_self_msat = value_to_self_msat.checked_sub(local_htlc_total_msat).unwrap();
3771+
value_to_remote_msat = value_to_remote_msat.checked_sub(remote_htlc_total_msat).unwrap();
37693772

37703773
#[cfg(debug_assertions)]
37713774
{
@@ -3776,33 +3779,40 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
37763779
} else {
37773780
funding.counterparty_max_commitment_tx_output.lock().unwrap()
37783781
};
3779-
debug_assert!(broadcaster_max_commitment_tx_output.0 <= value_to_self_msat as u64 || value_to_self_msat / 1000 >= funding.counterparty_selected_channel_reserve_satoshis.unwrap() as i64);
3780-
broadcaster_max_commitment_tx_output.0 = cmp::max(broadcaster_max_commitment_tx_output.0, value_to_self_msat as u64);
3781-
debug_assert!(broadcaster_max_commitment_tx_output.1 <= value_to_remote_msat as u64 || value_to_remote_msat / 1000 >= funding.holder_selected_channel_reserve_satoshis as i64);
3782-
broadcaster_max_commitment_tx_output.1 = cmp::max(broadcaster_max_commitment_tx_output.1, value_to_remote_msat as u64);
3782+
debug_assert!(broadcaster_max_commitment_tx_output.0 <= value_to_self_msat || value_to_self_msat / 1000 >= funding.counterparty_selected_channel_reserve_satoshis.unwrap());
3783+
broadcaster_max_commitment_tx_output.0 = cmp::max(broadcaster_max_commitment_tx_output.0, value_to_self_msat);
3784+
debug_assert!(broadcaster_max_commitment_tx_output.1 <= value_to_remote_msat || value_to_remote_msat / 1000 >= funding.holder_selected_channel_reserve_satoshis);
3785+
broadcaster_max_commitment_tx_output.1 = cmp::max(broadcaster_max_commitment_tx_output.1, value_to_remote_msat);
37833786
}
37843787

3788+
// We MUST use saturating subs here, as the funder's balance is not guaranteed to be greater
3789+
// than or equal to the sum of `total_fee_sat` and `anchors_val`.
3790+
//
3791+
// This is because when the remote party sends an `update_fee` message, we build the new
3792+
// commitment transaction *before* checking whether the remote party's balance is enough to
3793+
// cover the total fee and the anchors.
3794+
37853795
let total_fee_sat = commit_tx_fee_sat(feerate_per_kw, included_non_dust_htlcs.len(), &funding.channel_transaction_parameters.channel_type_features);
3786-
let anchors_val = if funding.channel_transaction_parameters.channel_type_features.supports_anchors_zero_fee_htlc_tx() { ANCHOR_OUTPUT_VALUE_SATOSHI * 2 } else { 0 } as i64;
3796+
let anchors_val = if funding.channel_transaction_parameters.channel_type_features.supports_anchors_zero_fee_htlc_tx() { ANCHOR_OUTPUT_VALUE_SATOSHI * 2 } else { 0 };
37873797
let (value_to_self, value_to_remote) = if funding.is_outbound() {
3788-
(value_to_self_msat / 1000 - anchors_val - total_fee_sat as i64, value_to_remote_msat / 1000)
3798+
((value_to_self_msat / 1000).saturating_sub(anchors_val).saturating_sub(total_fee_sat), value_to_remote_msat / 1000)
37893799
} else {
3790-
(value_to_self_msat / 1000, value_to_remote_msat / 1000 - anchors_val - total_fee_sat as i64)
3800+
(value_to_self_msat / 1000, (value_to_remote_msat / 1000).saturating_sub(anchors_val).saturating_sub(total_fee_sat))
37913801
};
37923802

3793-
let mut value_to_a = if local { value_to_self } else { value_to_remote };
3794-
let mut value_to_b = if local { value_to_remote } else { value_to_self };
3803+
let mut to_broadcaster_value_sat = if local { value_to_self } else { value_to_remote };
3804+
let mut to_countersignatory_value_sat = if local { value_to_remote } else { value_to_self };
37953805

3796-
if value_to_a >= (broadcaster_dust_limit_satoshis as i64) {
3797-
log_trace!(logger, " ...including {} output with value {}", if local { "to_local" } else { "to_remote" }, value_to_a);
3806+
if to_broadcaster_value_sat >= broadcaster_dust_limit_satoshis {
3807+
log_trace!(logger, " ...including {} output with value {}", if local { "to_local" } else { "to_remote" }, to_broadcaster_value_sat);
37983808
} else {
3799-
value_to_a = 0;
3809+
to_broadcaster_value_sat = 0;
38003810
}
38013811

3802-
if value_to_b >= (broadcaster_dust_limit_satoshis as i64) {
3803-
log_trace!(logger, " ...including {} output with value {}", if local { "to_remote" } else { "to_local" }, value_to_b);
3812+
if to_countersignatory_value_sat >= broadcaster_dust_limit_satoshis {
3813+
log_trace!(logger, " ...including {} output with value {}", if local { "to_remote" } else { "to_local" }, to_countersignatory_value_sat);
38043814
} else {
3805-
value_to_b = 0;
3815+
to_countersignatory_value_sat = 0;
38063816
}
38073817

38083818
let num_nondust_htlcs = included_non_dust_htlcs.len();
@@ -3812,8 +3822,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
38123822
else { funding.channel_transaction_parameters.as_counterparty_broadcastable() };
38133823
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(commitment_number,
38143824
per_commitment_point,
3815-
value_to_a as u64,
3816-
value_to_b as u64,
3825+
to_broadcaster_value_sat,
3826+
to_countersignatory_value_sat,
38173827
feerate_per_kw,
38183828
&mut included_non_dust_htlcs,
38193829
&channel_parameters,
@@ -3830,8 +3840,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
38303840
total_fee_sat,
38313841
num_nondust_htlcs,
38323842
htlcs_included,
3833-
local_balance_msat: value_to_self_msat as u64,
3834-
remote_balance_msat: value_to_remote_msat as u64,
3843+
local_balance_msat: value_to_self_msat,
3844+
remote_balance_msat: value_to_remote_msat,
38353845
inbound_htlc_preimages,
38363846
outbound_htlc_preimages,
38373847
}

0 commit comments

Comments
 (0)