@@ -1728,6 +1728,23 @@ struct PendingSplice {
1728
1728
pub our_funding_contribution: i64,
1729
1729
}
1730
1730
1731
+ #[cfg(splicing)]
1732
+ impl PendingSplice {
1733
+ #[inline]
1734
+ fn add_checked(base: u64, delta: i64) -> u64 {
1735
+ if delta >= 0 {
1736
+ base.saturating_add(delta as u64)
1737
+ } else {
1738
+ base.saturating_sub(delta.abs() as u64)
1739
+ }
1740
+ }
1741
+
1742
+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1743
+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1744
+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1745
+ }
1746
+ }
1747
+
1731
1748
/// Contains everything about the channel including state, and various flags.
1732
1749
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1733
1750
config: LegacyChannelConfig,
@@ -4256,6 +4273,59 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4256
4273
}
4257
4274
}
4258
4275
4276
+ /// Check that post-splicing balance meets reserve requirements, but only if it met it pre-splice as well
4277
+ #[cfg(splicing)]
4278
+ pub fn check_splice_balance_meets_v2_reserve_requirement_noerr(pre_balance: u64, post_balance: u64, pre_channel_value: u64, post_channel_value: u64, dust_limit: u64) -> Result<(), u64> {
4279
+ if post_balance == 0 {
4280
+ // 0 balance is fine
4281
+ return Ok(());
4282
+ }
4283
+ let post_channel_reserve = get_v2_channel_reserve_satoshis(post_channel_value, dust_limit);
4284
+ if post_balance >= post_channel_reserve {
4285
+ return Ok(());
4286
+ }
4287
+ // post is not OK, check pre
4288
+ if pre_balance == 0 {
4289
+ // pre OK, post not -> not
4290
+ return Err(post_channel_reserve);
4291
+ }
4292
+ let pre_channel_reserve = get_v2_channel_reserve_satoshis(pre_channel_value, dust_limit);
4293
+ if pre_balance >= pre_channel_reserve {
4294
+ // pre OK, post not -> not
4295
+ return Err(post_channel_reserve);
4296
+ }
4297
+ // post not OK, but so was pre -> OK
4298
+ Ok(())
4299
+ }
4300
+
4301
+ /// Check that balances meet the channel reserve requirements or violates them (below reserve).
4302
+ /// The channel value is an input as opposed to using from the FundingScope, so that this can be used in case of splicing
4303
+ /// to check with new channel value (before being committed to it).
4304
+ #[cfg(splicing)]
4305
+ pub fn check_splice_balances_meet_v2_reserve_requirements(&self, self_balance_pre: u64, self_balance_post: u64, counterparty_balance_pre: u64, counterparty_balance_post: u64, channel_value_pre: u64, channel_value_post: u64) -> Result<(), ChannelError> {
4306
+ let is_ok_self = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
4307
+ self_balance_pre, self_balance_post, channel_value_pre, channel_value_post,
4308
+ self.holder_dust_limit_satoshis
4309
+ );
4310
+ if let Err(channel_reserve_self) = is_ok_self {
4311
+ return Err(ChannelError::Warn(format!(
4312
+ "Balance below reserve, mandated by holder, {} vs {}",
4313
+ self_balance_post, channel_reserve_self,
4314
+ )));
4315
+ }
4316
+ let is_ok_cp = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
4317
+ counterparty_balance_pre, counterparty_balance_post, channel_value_pre, channel_value_post,
4318
+ self.counterparty_dust_limit_satoshis
4319
+ );
4320
+ if let Err(channel_reserve_cp) = is_ok_cp {
4321
+ return Err(ChannelError::Warn(format!(
4322
+ "Balance below reserve mandated by counterparty, {} vs {}",
4323
+ counterparty_balance_post, channel_reserve_cp,
4324
+ )));
4325
+ }
4326
+ Ok(())
4327
+ }
4328
+
4259
4329
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
4260
4330
/// number of pending HTLCs that are on track to be in our next commitment tx.
4261
4331
///
@@ -8550,14 +8620,28 @@ impl<SP: Deref> FundedChannel<SP> where
8550
8620
8551
8621
/// Handle splice_ack
8552
8622
#[cfg(splicing)]
8553
- pub fn splice_ack(&mut self, _msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
8623
+ pub fn splice_ack(&mut self, msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
8554
8624
// check if splice is pending
8555
- if self.pending_splice.is_none() {
8625
+ let pending_splice = if let Some(pending_splice) = &self.pending_splice {
8626
+ pending_splice
8627
+ } else {
8556
8628
return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8557
8629
};
8558
8630
8559
- // TODO(splicing): Pre-check for reserve requirement
8631
+ // Pre-check for reserve requirement
8560
8632
// (Note: It should also be checked later at tx_complete)
8633
+ let our_funding_contribution = pending_splice.our_funding_contribution;
8634
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8635
+
8636
+ let pre_channel_value = self.funding.get_value_satoshis();
8637
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8638
+ let pre_balance_self = self.funding.value_to_self_msat;
8639
+ let post_balance_self = PendingSplice::add_checked(pre_balance_self, our_funding_contribution);
8640
+ let pre_balance_counterparty = pre_channel_value.saturating_sub(pre_balance_self);
8641
+ let post_balance_counterparty = post_channel_value.saturating_sub(post_balance_self);
8642
+ // Pre-check for reserve requirement
8643
+ // This will also be checked later at tx_complete
8644
+ let _res = self.context.check_splice_balances_meet_v2_reserve_requirements(pre_balance_self, post_balance_self, pre_balance_counterparty, post_balance_counterparty, pre_channel_value, post_channel_value)?;
8561
8645
Ok(())
8562
8646
}
8563
8647
@@ -13050,4 +13134,69 @@ mod tests {
13050
13134
);
13051
13135
}
13052
13136
}
13137
+
13138
+ #[cfg(splicing)]
13139
+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
13140
+ use crate::ln::channel::PendingSplice;
13141
+
13142
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
13143
+ (pre_channel_value, post_channel_value)
13144
+ }
13145
+
13146
+ #[cfg(splicing)]
13147
+ #[test]
13148
+ fn test_splice_compute_post_value() {
13149
+ {
13150
+ // increase, small amounts
13151
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
13152
+ assert_eq!(pre_channel_value, 9_000);
13153
+ assert_eq!(post_channel_value, 15_000);
13154
+ }
13155
+ {
13156
+ // increase, small amounts
13157
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
13158
+ assert_eq!(pre_channel_value, 9_000);
13159
+ assert_eq!(post_channel_value, 15_000);
13160
+ }
13161
+ {
13162
+ // increase, small amounts
13163
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
13164
+ assert_eq!(pre_channel_value, 9_000);
13165
+ assert_eq!(post_channel_value, 15_000);
13166
+ }
13167
+ {
13168
+ // decrease, small amounts
13169
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
13170
+ assert_eq!(pre_channel_value, 15_000);
13171
+ assert_eq!(post_channel_value, 9_000);
13172
+ }
13173
+ {
13174
+ // decrease, small amounts
13175
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
13176
+ assert_eq!(pre_channel_value, 15_000);
13177
+ assert_eq!(post_channel_value, 9_000);
13178
+ }
13179
+ {
13180
+ // increase and decrease
13181
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
13182
+ assert_eq!(pre_channel_value, 15_000);
13183
+ assert_eq!(post_channel_value, 17_000);
13184
+ }
13185
+ let base2: u64 = 2;
13186
+ let huge63i3 = (base2.pow(63) - 3) as i64;
13187
+ assert_eq!(huge63i3, 9223372036854775805);
13188
+ assert_eq!(-huge63i3, -9223372036854775805);
13189
+ {
13190
+ // increase, large amount
13191
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
13192
+ assert_eq!(pre_channel_value, 9_000);
13193
+ assert_eq!(post_channel_value, 9223372036854784807);
13194
+ }
13195
+ {
13196
+ // increase, large amounts
13197
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
13198
+ assert_eq!(pre_channel_value, 9_000);
13199
+ assert_eq!(post_channel_value, 9223372036854784807);
13200
+ }
13201
+ }
13053
13202
}
0 commit comments