@@ -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,
@@ -4249,6 +4266,59 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4249
4266
}
4250
4267
}
4251
4268
4269
+ /// Check that post-splicing balance meets reserve requirements, but only if it met it pre-splice as well
4270
+ #[cfg(splicing)]
4271
+ 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> {
4272
+ if post_balance == 0 {
4273
+ // 0 balance is fine
4274
+ return Ok(());
4275
+ }
4276
+ let post_channel_reserve = get_v2_channel_reserve_satoshis(post_channel_value, dust_limit);
4277
+ if post_balance >= post_channel_reserve {
4278
+ return Ok(());
4279
+ }
4280
+ // post is not OK, check pre
4281
+ if pre_balance == 0 {
4282
+ // pre OK, post not -> not
4283
+ return Err(post_channel_reserve);
4284
+ }
4285
+ let pre_channel_reserve = get_v2_channel_reserve_satoshis(pre_channel_value, dust_limit);
4286
+ if pre_balance >= pre_channel_reserve {
4287
+ // pre OK, post not -> not
4288
+ return Err(post_channel_reserve);
4289
+ }
4290
+ // post not OK, but so was pre -> OK
4291
+ Ok(())
4292
+ }
4293
+
4294
+ /// Check that balances meet the channel reserve requirements or violates them (below reserve).
4295
+ /// The channel value is an input as opposed to using from the FundingScope, so that this can be used in case of splicing
4296
+ /// to check with new channel value (before being committed to it).
4297
+ #[cfg(splicing)]
4298
+ 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> {
4299
+ let is_ok_self = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
4300
+ self_balance_pre, self_balance_post, channel_value_pre, channel_value_post,
4301
+ self.holder_dust_limit_satoshis
4302
+ );
4303
+ if let Err(channel_reserve_self) = is_ok_self {
4304
+ return Err(ChannelError::Warn(format!(
4305
+ "Balance below reserve, mandated by holder, {} vs {}",
4306
+ self_balance_post, channel_reserve_self,
4307
+ )));
4308
+ }
4309
+ let is_ok_cp = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
4310
+ counterparty_balance_pre, counterparty_balance_post, channel_value_pre, channel_value_post,
4311
+ self.counterparty_dust_limit_satoshis
4312
+ );
4313
+ if let Err(channel_reserve_cp) = is_ok_cp {
4314
+ return Err(ChannelError::Warn(format!(
4315
+ "Balance below reserve mandated by counterparty, {} vs {}",
4316
+ counterparty_balance_post, channel_reserve_cp,
4317
+ )));
4318
+ }
4319
+ Ok(())
4320
+ }
4321
+
4252
4322
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
4253
4323
/// number of pending HTLCs that are on track to be in our next commitment tx.
4254
4324
///
@@ -8542,14 +8612,28 @@ impl<SP: Deref> FundedChannel<SP> where
8542
8612
8543
8613
/// Handle splice_ack
8544
8614
#[cfg(splicing)]
8545
- pub fn splice_ack(&mut self, _msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
8615
+ pub fn splice_ack(&mut self, msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
8546
8616
// check if splice is pending
8547
- if self.pending_splice.is_none() {
8617
+ let pending_splice = if let Some(pending_splice) = &self.pending_splice {
8618
+ pending_splice
8619
+ } else {
8548
8620
return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8549
8621
};
8550
8622
8551
- // TODO(splicing): Pre-check for reserve requirement
8623
+ // Pre-check for reserve requirement
8552
8624
// (Note: It should also be checked later at tx_complete)
8625
+ let our_funding_contribution = pending_splice.our_funding_contribution;
8626
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8627
+
8628
+ let pre_channel_value = self.funding.get_value_satoshis();
8629
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8630
+ let pre_balance_self = self.funding.value_to_self_msat;
8631
+ let post_balance_self = PendingSplice::add_checked(pre_balance_self, our_funding_contribution);
8632
+ let pre_balance_counterparty = pre_channel_value.saturating_sub(pre_balance_self);
8633
+ let post_balance_counterparty = post_channel_value.saturating_sub(post_balance_self);
8634
+ // Pre-check for reserve requirement
8635
+ // This will also be checked later at tx_complete
8636
+ 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)?;
8553
8637
Ok(())
8554
8638
}
8555
8639
@@ -13061,4 +13145,69 @@ mod tests {
13061
13145
);
13062
13146
}
13063
13147
}
13148
+
13149
+ #[cfg(splicing)]
13150
+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
13151
+ use crate::ln::channel::PendingSplice;
13152
+
13153
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
13154
+ (pre_channel_value, post_channel_value)
13155
+ }
13156
+
13157
+ #[cfg(splicing)]
13158
+ #[test]
13159
+ fn test_splice_compute_post_value() {
13160
+ {
13161
+ // increase, small amounts
13162
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
13163
+ assert_eq!(pre_channel_value, 9_000);
13164
+ assert_eq!(post_channel_value, 15_000);
13165
+ }
13166
+ {
13167
+ // increase, small amounts
13168
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
13169
+ assert_eq!(pre_channel_value, 9_000);
13170
+ assert_eq!(post_channel_value, 15_000);
13171
+ }
13172
+ {
13173
+ // increase, small amounts
13174
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
13175
+ assert_eq!(pre_channel_value, 9_000);
13176
+ assert_eq!(post_channel_value, 15_000);
13177
+ }
13178
+ {
13179
+ // decrease, small amounts
13180
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
13181
+ assert_eq!(pre_channel_value, 15_000);
13182
+ assert_eq!(post_channel_value, 9_000);
13183
+ }
13184
+ {
13185
+ // decrease, small amounts
13186
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
13187
+ assert_eq!(pre_channel_value, 15_000);
13188
+ assert_eq!(post_channel_value, 9_000);
13189
+ }
13190
+ {
13191
+ // increase and decrease
13192
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
13193
+ assert_eq!(pre_channel_value, 15_000);
13194
+ assert_eq!(post_channel_value, 17_000);
13195
+ }
13196
+ let base2: u64 = 2;
13197
+ let huge63i3 = (base2.pow(63) - 3) as i64;
13198
+ assert_eq!(huge63i3, 9223372036854775805);
13199
+ assert_eq!(-huge63i3, -9223372036854775805);
13200
+ {
13201
+ // increase, large amount
13202
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
13203
+ assert_eq!(pre_channel_value, 9_000);
13204
+ assert_eq!(post_channel_value, 9223372036854784807);
13205
+ }
13206
+ {
13207
+ // increase, large amounts
13208
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
13209
+ assert_eq!(pre_channel_value, 9_000);
13210
+ assert_eq!(post_channel_value, 9223372036854784807);
13211
+ }
13212
+ }
13064
13213
}
0 commit comments