Skip to content

Commit 1005d00

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 1c4a496 commit 1005d00

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
@@ -3760,14 +3760,17 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
37603760
}
37613761
}
37623762

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

37723775
#[cfg(debug_assertions)]
37733776
{
@@ -3778,33 +3781,40 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
37783781
} else {
37793782
funding.counterparty_max_commitment_tx_output.lock().unwrap()
37803783
};
3781-
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);
3782-
broadcaster_max_commitment_tx_output.0 = cmp::max(broadcaster_max_commitment_tx_output.0, value_to_self_msat as u64);
3783-
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);
3784-
broadcaster_max_commitment_tx_output.1 = cmp::max(broadcaster_max_commitment_tx_output.1, value_to_remote_msat as u64);
3784+
debug_assert!(broadcaster_max_commitment_tx_output.0 <= value_to_self_msat || value_to_self_msat / 1000 >= funding.counterparty_selected_channel_reserve_satoshis.unwrap());
3785+
broadcaster_max_commitment_tx_output.0 = cmp::max(broadcaster_max_commitment_tx_output.0, value_to_self_msat);
3786+
debug_assert!(broadcaster_max_commitment_tx_output.1 <= value_to_remote_msat || value_to_remote_msat / 1000 >= funding.holder_selected_channel_reserve_satoshis);
3787+
broadcaster_max_commitment_tx_output.1 = cmp::max(broadcaster_max_commitment_tx_output.1, value_to_remote_msat);
37853788
}
37863789

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

3795-
let mut value_to_a = if local { value_to_self } else { value_to_remote };
3796-
let mut value_to_b = if local { value_to_remote } else { value_to_self };
3805+
let mut to_broadcaster_value_sat = if local { value_to_self } else { value_to_remote };
3806+
let mut to_countersignatory_value_sat = if local { value_to_remote } else { value_to_self };
37973807

3798-
if value_to_a >= (broadcaster_dust_limit_satoshis as i64) {
3799-
log_trace!(logger, " ...including {} output with value {}", if local { "to_local" } else { "to_remote" }, value_to_a);
3808+
if to_broadcaster_value_sat >= broadcaster_dust_limit_satoshis {
3809+
log_trace!(logger, " ...including {} output with value {}", if local { "to_local" } else { "to_remote" }, to_broadcaster_value_sat);
38003810
} else {
3801-
value_to_a = 0;
3811+
to_broadcaster_value_sat = 0;
38023812
}
38033813

3804-
if value_to_b >= (broadcaster_dust_limit_satoshis as i64) {
3805-
log_trace!(logger, " ...including {} output with value {}", if local { "to_remote" } else { "to_local" }, value_to_b);
3814+
if to_countersignatory_value_sat >= broadcaster_dust_limit_satoshis {
3815+
log_trace!(logger, " ...including {} output with value {}", if local { "to_remote" } else { "to_local" }, to_countersignatory_value_sat);
38063816
} else {
3807-
value_to_b = 0;
3817+
to_countersignatory_value_sat = 0;
38083818
}
38093819

38103820
let num_nondust_htlcs = included_non_dust_htlcs.len();
@@ -3814,8 +3824,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
38143824
else { funding.channel_transaction_parameters.as_counterparty_broadcastable() };
38153825
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(commitment_number,
38163826
per_commitment_point,
3817-
value_to_a as u64,
3818-
value_to_b as u64,
3827+
to_broadcaster_value_sat,
3828+
to_countersignatory_value_sat,
38193829
feerate_per_kw,
38203830
&mut included_non_dust_htlcs,
38213831
&channel_parameters,
@@ -3832,8 +3842,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
38323842
total_fee_sat,
38333843
num_nondust_htlcs,
38343844
htlcs_included,
3835-
local_balance_msat: value_to_self_msat as u64,
3836-
remote_balance_msat: value_to_remote_msat as u64,
3845+
local_balance_msat: value_to_self_msat,
3846+
remote_balance_msat: value_to_remote_msat,
38373847
inbound_htlc_preimages,
38383848
outbound_htlc_preimages,
38393849
}

0 commit comments

Comments
 (0)