@@ -1522,7 +1522,7 @@ impl<SP: Deref> Channel<SP> where
1522
1522
holder_commitment_point,
1523
1523
is_v2_established: true,
1524
1524
#[cfg(splicing)]
1525
- pending_splice : None,
1525
+ pending_splice_pre : None,
1526
1526
};
1527
1527
let res = funded_channel.commitment_signed_initial_v2(msg, best_block, signer_provider, logger)
1528
1528
.map(|monitor| (Some(monitor), None))
@@ -1731,6 +1731,23 @@ struct PendingSplice {
1731
1731
pub our_funding_contribution: i64,
1732
1732
}
1733
1733
1734
+ #[cfg(splicing)]
1735
+ impl PendingSplice {
1736
+ #[inline]
1737
+ fn add_checked(base: u64, delta: i64) -> u64 {
1738
+ if delta >= 0 {
1739
+ base.saturating_add(delta as u64)
1740
+ } else {
1741
+ base.saturating_sub(delta.abs() as u64)
1742
+ }
1743
+ }
1744
+
1745
+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1746
+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1747
+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1748
+ }
1749
+ }
1750
+
1734
1751
/// Contains everything about the channel including state, and various flags.
1735
1752
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1736
1753
config: LegacyChannelConfig,
@@ -4213,6 +4230,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4213
4230
}
4214
4231
}
4215
4232
4233
+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
4234
+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
4235
+ /// to checks with new channel value (before being comitted to it).
4236
+ #[cfg(splicing)]
4237
+ pub fn check_balance_meets_v2_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
4238
+ if balance == 0 {
4239
+ return Ok(());
4240
+ }
4241
+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4242
+ channel_value, self.holder_dust_limit_satoshis);
4243
+ if balance < holder_selected_channel_reserve_satoshis {
4244
+ return Err(ChannelError::Warn(format!(
4245
+ "Balance below reserve mandated by holder, {} vs {}",
4246
+ balance, holder_selected_channel_reserve_satoshis,
4247
+ )));
4248
+ }
4249
+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4250
+ channel_value, self.counterparty_dust_limit_satoshis);
4251
+ if balance < counterparty_selected_channel_reserve_satoshis {
4252
+ return Err(ChannelError::Warn(format!(
4253
+ "Balance below reserve mandated by counterparty, {} vs {}",
4254
+ balance, counterparty_selected_channel_reserve_satoshis,
4255
+ )));
4256
+ }
4257
+ Ok(())
4258
+ }
4259
+
4216
4260
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
4217
4261
/// number of pending HTLCs that are on track to be in our next commitment tx.
4218
4262
///
@@ -4860,7 +4904,7 @@ pub(super) struct FundedChannel<SP: Deref> where SP::Target: SignerProvider {
4860
4904
is_v2_established: bool,
4861
4905
/// Info about an in-progress, pending splice (if any), on the pre-splice channel
4862
4906
#[cfg(splicing)]
4863
- pending_splice : Option<PendingSplice>,
4907
+ pending_splice_pre : Option<PendingSplice>,
4864
4908
}
4865
4909
4866
4910
#[cfg(any(test, fuzzing))]
@@ -8395,7 +8439,7 @@ impl<SP: Deref> FundedChannel<SP> where
8395
8439
) -> Result<msgs::SpliceInit, APIError> {
8396
8440
// Check if a splice has been initiated already.
8397
8441
// Note: only a single outstanding splice is supported (per spec)
8398
- if let Some(splice_info) = &self.pending_splice {
8442
+ if let Some(splice_info) = &self.pending_splice_pre {
8399
8443
return Err(APIError::APIMisuseError { err: format!(
8400
8444
"Channel {} cannot be spliced, as it has already a splice pending (contribution {})",
8401
8445
self.context.channel_id(), splice_info.our_funding_contribution
@@ -8431,7 +8475,7 @@ impl<SP: Deref> FundedChannel<SP> where
8431
8475
self.context.channel_id(), err,
8432
8476
)})?;
8433
8477
8434
- self.pending_splice = Some(PendingSplice {
8478
+ self.pending_splice_pre = Some(PendingSplice {
8435
8479
our_funding_contribution: our_funding_contribution_satoshis,
8436
8480
});
8437
8481
@@ -8465,7 +8509,7 @@ impl<SP: Deref> FundedChannel<SP> where
8465
8509
let our_funding_contribution_satoshis = 0i64;
8466
8510
8467
8511
// Check if a splice has been initiated already.
8468
- if let Some(splice_info) = &self.pending_splice {
8512
+ if let Some(splice_info) = &self.pending_splice_pre {
8469
8513
return Err(ChannelError::Warn(format!(
8470
8514
"Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
8471
8515
)));
@@ -8491,7 +8535,13 @@ impl<SP: Deref> FundedChannel<SP> where
8491
8535
8492
8536
// Note on channel reserve requirement pre-check: as the splice acceptor does not contribute,
8493
8537
// it can't go below reserve, therefore no pre-check is done here.
8494
- // TODO(splicing): Once splice acceptor can contribute, add reserve pre-check, similar to the one in `splice_ack`.
8538
+
8539
+ let pre_channel_value = self.funding.value_to_self_msat;
8540
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
8541
+ let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution_satoshis);
8542
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8543
+ // This will also be checked later at tx_complete
8544
+ let _res = self.context.check_balance_meets_v2_reserve_requirements(post_balance, post_channel_value)?;
8495
8545
8496
8546
// TODO(splicing): Store msg.funding_pubkey
8497
8547
// TODO(splicing): Apply start of splice (splice_start)
@@ -8510,14 +8560,27 @@ impl<SP: Deref> FundedChannel<SP> where
8510
8560
8511
8561
/// Handle splice_ack
8512
8562
#[cfg(splicing)]
8513
- pub fn splice_ack(&mut self, _msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
8563
+ pub fn splice_ack(&mut self, msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
8514
8564
// check if splice is pending
8515
- if self.pending_splice.is_none() {
8565
+ let pending_splice = if let Some(pending_splice) = &self.pending_splice_pre {
8566
+ pending_splice
8567
+ } else {
8516
8568
return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8517
8569
};
8518
8570
8519
8571
// TODO(splicing): Pre-check for reserve requirement
8520
8572
// (Note: It should also be checked later at tx_complete)
8573
+
8574
+
8575
+ let our_funding_contribution = pending_splice.our_funding_contribution;
8576
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8577
+
8578
+ let pre_channel_value = self.funding.get_value_satoshis();
8579
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8580
+ let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution);
8581
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8582
+ // This will also be checked later at tx_complete
8583
+ let _res = self.context.check_balance_meets_v2_reserve_requirements(post_balance, post_channel_value)?;
8521
8584
Ok(())
8522
8585
}
8523
8586
@@ -9444,7 +9507,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
9444
9507
is_v2_established: false,
9445
9508
holder_commitment_point,
9446
9509
#[cfg(splicing)]
9447
- pending_splice : None,
9510
+ pending_splice_pre : None,
9448
9511
};
9449
9512
9450
9513
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
@@ -9720,7 +9783,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
9720
9783
is_v2_established: false,
9721
9784
holder_commitment_point,
9722
9785
#[cfg(splicing)]
9723
- pending_splice : None,
9786
+ pending_splice_pre : None,
9724
9787
};
9725
9788
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
9726
9789
|| channel.context.signer_pending_channel_ready;
@@ -11101,7 +11164,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
11101
11164
is_v2_established,
11102
11165
holder_commitment_point,
11103
11166
#[cfg(splicing)]
11104
- pending_splice : None,
11167
+ pending_splice_pre : None,
11105
11168
})
11106
11169
}
11107
11170
}
@@ -13023,4 +13086,69 @@ mod tests {
13023
13086
);
13024
13087
}
13025
13088
}
13089
+
13090
+ #[cfg(all(test, splicing))]
13091
+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
13092
+ use crate::ln::channel::PendingSplice;
13093
+
13094
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
13095
+ (pre_channel_value, post_channel_value)
13096
+ }
13097
+
13098
+ #[cfg(all(test, splicing))]
13099
+ #[test]
13100
+ fn test_splice_compute_post_value() {
13101
+ {
13102
+ // increase, small amounts
13103
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
13104
+ assert_eq!(pre_channel_value, 9_000);
13105
+ assert_eq!(post_channel_value, 15_000);
13106
+ }
13107
+ {
13108
+ // increase, small amounts
13109
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
13110
+ assert_eq!(pre_channel_value, 9_000);
13111
+ assert_eq!(post_channel_value, 15_000);
13112
+ }
13113
+ {
13114
+ // increase, small amounts
13115
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
13116
+ assert_eq!(pre_channel_value, 9_000);
13117
+ assert_eq!(post_channel_value, 15_000);
13118
+ }
13119
+ {
13120
+ // decrease, small amounts
13121
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
13122
+ assert_eq!(pre_channel_value, 15_000);
13123
+ assert_eq!(post_channel_value, 9_000);
13124
+ }
13125
+ {
13126
+ // decrease, small amounts
13127
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
13128
+ assert_eq!(pre_channel_value, 15_000);
13129
+ assert_eq!(post_channel_value, 9_000);
13130
+ }
13131
+ {
13132
+ // increase and decrease
13133
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
13134
+ assert_eq!(pre_channel_value, 15_000);
13135
+ assert_eq!(post_channel_value, 17_000);
13136
+ }
13137
+ let base2: u64 = 2;
13138
+ let huge63i3 = (base2.pow(63) - 3) as i64;
13139
+ assert_eq!(huge63i3, 9223372036854775805);
13140
+ assert_eq!(-huge63i3, -9223372036854775805);
13141
+ {
13142
+ // increase, large amount
13143
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
13144
+ assert_eq!(pre_channel_value, 9_000);
13145
+ assert_eq!(post_channel_value, 9223372036854784807);
13146
+ }
13147
+ {
13148
+ // increase, large amounts
13149
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
13150
+ assert_eq!(pre_channel_value, 9_000);
13151
+ assert_eq!(post_channel_value, 9223372036854784807);
13152
+ }
13153
+ }
13026
13154
}
0 commit comments