@@ -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))
@@ -1736,6 +1736,23 @@ struct PendingSplice {
1736
1736
pub our_funding_contribution: i64,
1737
1737
}
1738
1738
1739
+ #[cfg(splicing)]
1740
+ impl PendingSplice {
1741
+ #[inline]
1742
+ fn add_checked(base: u64, delta: i64) -> u64 {
1743
+ if delta >= 0 {
1744
+ base.saturating_add(delta as u64)
1745
+ } else {
1746
+ base.saturating_sub(delta.abs() as u64)
1747
+ }
1748
+ }
1749
+
1750
+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1751
+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1752
+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1753
+ }
1754
+ }
1755
+
1739
1756
/// Contains everything about the channel including state, and various flags.
1740
1757
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1741
1758
config: LegacyChannelConfig,
@@ -4299,6 +4316,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4299
4316
}
4300
4317
}
4301
4318
4319
+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
4320
+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
4321
+ /// to checks with new channel value (before being comitted to it).
4322
+ #[cfg(splicing)]
4323
+ pub fn check_balance_meets_v2_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
4324
+ if balance == 0 {
4325
+ return Ok(());
4326
+ }
4327
+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4328
+ channel_value, self.holder_dust_limit_satoshis);
4329
+ if balance < holder_selected_channel_reserve_satoshis {
4330
+ return Err(ChannelError::Warn(format!(
4331
+ "Balance below reserve mandated by holder, {} vs {}",
4332
+ balance, holder_selected_channel_reserve_satoshis,
4333
+ )));
4334
+ }
4335
+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4336
+ channel_value, self.counterparty_dust_limit_satoshis);
4337
+ if balance < counterparty_selected_channel_reserve_satoshis {
4338
+ return Err(ChannelError::Warn(format!(
4339
+ "Balance below reserve mandated by counterparty, {} vs {}",
4340
+ balance, counterparty_selected_channel_reserve_satoshis,
4341
+ )));
4342
+ }
4343
+ Ok(())
4344
+ }
4345
+
4302
4346
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
4303
4347
/// number of pending HTLCs that are on track to be in our next commitment tx.
4304
4348
///
@@ -4951,7 +4995,7 @@ pub(super) struct FundedChannel<SP: Deref> where SP::Target: SignerProvider {
4951
4995
is_v2_established: bool,
4952
4996
/// Info about an in-progress, pending splice (if any), on the pre-splice channel
4953
4997
#[cfg(splicing)]
4954
- pending_splice : Option<PendingSplice>,
4998
+ pending_splice_pre : Option<PendingSplice>,
4955
4999
}
4956
5000
4957
5001
#[cfg(any(test, fuzzing))]
@@ -8487,7 +8531,7 @@ impl<SP: Deref> FundedChannel<SP> where
8487
8531
) -> Result<msgs::SpliceInit, APIError> {
8488
8532
// Check if a splice has been initiated already.
8489
8533
// Note: only a single outstanding splice is supported (per spec)
8490
- if let Some(splice_info) = &self.pending_splice {
8534
+ if let Some(splice_info) = &self.pending_splice_pre {
8491
8535
return Err(APIError::APIMisuseError { err: format!(
8492
8536
"Channel {} cannot be spliced, as it has already a splice pending (contribution {})",
8493
8537
self.context.channel_id(), splice_info.our_funding_contribution
@@ -8523,7 +8567,7 @@ impl<SP: Deref> FundedChannel<SP> where
8523
8567
self.context.channel_id(), err,
8524
8568
)})?;
8525
8569
8526
- self.pending_splice = Some(PendingSplice {
8570
+ self.pending_splice_pre = Some(PendingSplice {
8527
8571
our_funding_contribution: our_funding_contribution_satoshis,
8528
8572
});
8529
8573
@@ -8557,7 +8601,7 @@ impl<SP: Deref> FundedChannel<SP> where
8557
8601
let our_funding_contribution_satoshis = 0i64;
8558
8602
8559
8603
// Check if a splice has been initiated already.
8560
- if let Some(splice_info) = &self.pending_splice {
8604
+ if let Some(splice_info) = &self.pending_splice_pre {
8561
8605
return Err(ChannelError::Warn(format!(
8562
8606
"Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
8563
8607
)));
@@ -8583,7 +8627,13 @@ impl<SP: Deref> FundedChannel<SP> where
8583
8627
8584
8628
// Note on channel reserve requirement pre-check: as the splice acceptor does not contribute,
8585
8629
// it can't go below reserve, therefore no pre-check is done here.
8586
- // TODO(splicing): Once splice acceptor can contribute, add reserve pre-check, similar to the one in `splice_ack`.
8630
+
8631
+ let pre_channel_value = self.funding.value_to_self_msat;
8632
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
8633
+ let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution_satoshis);
8634
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8635
+ // This will also be checked later at tx_complete
8636
+ let _res = self.context.check_balance_meets_v2_reserve_requirements(post_balance, post_channel_value)?;
8587
8637
8588
8638
// TODO(splicing): Store msg.funding_pubkey
8589
8639
// TODO(splicing): Apply start of splice (splice_start)
@@ -8602,14 +8652,27 @@ impl<SP: Deref> FundedChannel<SP> where
8602
8652
8603
8653
/// Handle splice_ack
8604
8654
#[cfg(splicing)]
8605
- pub fn splice_ack(&mut self, _msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
8655
+ pub fn splice_ack(&mut self, msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
8606
8656
// check if splice is pending
8607
- if self.pending_splice.is_none() {
8657
+ let pending_splice = if let Some(pending_splice) = &self.pending_splice_pre {
8658
+ pending_splice
8659
+ } else {
8608
8660
return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8609
8661
};
8610
8662
8611
8663
// TODO(splicing): Pre-check for reserve requirement
8612
8664
// (Note: It should also be checked later at tx_complete)
8665
+
8666
+
8667
+ let our_funding_contribution = pending_splice.our_funding_contribution;
8668
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8669
+
8670
+ let pre_channel_value = self.funding.get_value_satoshis();
8671
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8672
+ let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution);
8673
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8674
+ // This will also be checked later at tx_complete
8675
+ let _res = self.context.check_balance_meets_v2_reserve_requirements(post_balance, post_channel_value)?;
8613
8676
Ok(())
8614
8677
}
8615
8678
@@ -9536,7 +9599,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
9536
9599
is_v2_established: false,
9537
9600
holder_commitment_point,
9538
9601
#[cfg(splicing)]
9539
- pending_splice : None,
9602
+ pending_splice_pre : None,
9540
9603
};
9541
9604
9542
9605
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
@@ -9812,7 +9875,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
9812
9875
is_v2_established: false,
9813
9876
holder_commitment_point,
9814
9877
#[cfg(splicing)]
9815
- pending_splice : None,
9878
+ pending_splice_pre : None,
9816
9879
};
9817
9880
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
9818
9881
|| channel.context.signer_pending_channel_ready;
@@ -11196,7 +11259,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
11196
11259
is_v2_established,
11197
11260
holder_commitment_point,
11198
11261
#[cfg(splicing)]
11199
- pending_splice : None,
11262
+ pending_splice_pre : None,
11200
11263
})
11201
11264
}
11202
11265
}
@@ -13118,4 +13181,69 @@ mod tests {
13118
13181
);
13119
13182
}
13120
13183
}
13184
+
13185
+ #[cfg(all(test, splicing))]
13186
+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
13187
+ use crate::ln::channel::PendingSplice;
13188
+
13189
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
13190
+ (pre_channel_value, post_channel_value)
13191
+ }
13192
+
13193
+ #[cfg(all(test, splicing))]
13194
+ #[test]
13195
+ fn test_splice_compute_post_value() {
13196
+ {
13197
+ // increase, small amounts
13198
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
13199
+ assert_eq!(pre_channel_value, 9_000);
13200
+ assert_eq!(post_channel_value, 15_000);
13201
+ }
13202
+ {
13203
+ // increase, small amounts
13204
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
13205
+ assert_eq!(pre_channel_value, 9_000);
13206
+ assert_eq!(post_channel_value, 15_000);
13207
+ }
13208
+ {
13209
+ // increase, small amounts
13210
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
13211
+ assert_eq!(pre_channel_value, 9_000);
13212
+ assert_eq!(post_channel_value, 15_000);
13213
+ }
13214
+ {
13215
+ // decrease, small amounts
13216
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
13217
+ assert_eq!(pre_channel_value, 15_000);
13218
+ assert_eq!(post_channel_value, 9_000);
13219
+ }
13220
+ {
13221
+ // decrease, small amounts
13222
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
13223
+ assert_eq!(pre_channel_value, 15_000);
13224
+ assert_eq!(post_channel_value, 9_000);
13225
+ }
13226
+ {
13227
+ // increase and decrease
13228
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
13229
+ assert_eq!(pre_channel_value, 15_000);
13230
+ assert_eq!(post_channel_value, 17_000);
13231
+ }
13232
+ let base2: u64 = 2;
13233
+ let huge63i3 = (base2.pow(63) - 3) as i64;
13234
+ assert_eq!(huge63i3, 9223372036854775805);
13235
+ assert_eq!(-huge63i3, -9223372036854775805);
13236
+ {
13237
+ // increase, large amount
13238
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
13239
+ assert_eq!(pre_channel_value, 9_000);
13240
+ assert_eq!(post_channel_value, 9223372036854784807);
13241
+ }
13242
+ {
13243
+ // increase, large amounts
13244
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
13245
+ assert_eq!(pre_channel_value, 9_000);
13246
+ assert_eq!(post_channel_value, 9223372036854784807);
13247
+ }
13248
+ }
13121
13249
}
0 commit comments