@@ -2242,6 +2242,23 @@ impl<'a> From<&'a Transaction> for ConfirmedTransaction<'a> {
2242
2242
}
2243
2243
}
2244
2244
2245
+ #[cfg(splicing)]
2246
+ impl PendingSplice {
2247
+ #[inline]
2248
+ fn add_checked(base: u64, delta: i64) -> u64 {
2249
+ if delta >= 0 {
2250
+ base.saturating_add(delta as u64)
2251
+ } else {
2252
+ base.saturating_sub(delta.abs() as u64)
2253
+ }
2254
+ }
2255
+
2256
+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
2257
+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
2258
+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
2259
+ }
2260
+ }
2261
+
2245
2262
/// Contains everything about the channel including state, and various flags.
2246
2263
pub(super) struct ChannelContext<SP: Deref>
2247
2264
where
@@ -5098,6 +5115,59 @@ where
5098
5115
}
5099
5116
}
5100
5117
5118
+ /// Check that post-splicing balance meets reserve requirements, but only if it met it pre-splice as well
5119
+ #[cfg(splicing)]
5120
+ 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> {
5121
+ if post_balance == 0 {
5122
+ // 0 balance is fine
5123
+ return Ok(());
5124
+ }
5125
+ let post_channel_reserve = get_v2_channel_reserve_satoshis(post_channel_value, dust_limit);
5126
+ if post_balance >= post_channel_reserve {
5127
+ return Ok(());
5128
+ }
5129
+ // post is not OK, check pre
5130
+ if pre_balance == 0 {
5131
+ // pre OK, post not -> not
5132
+ return Err(post_channel_reserve);
5133
+ }
5134
+ let pre_channel_reserve = get_v2_channel_reserve_satoshis(pre_channel_value, dust_limit);
5135
+ if pre_balance >= pre_channel_reserve {
5136
+ // pre OK, post not -> not
5137
+ return Err(post_channel_reserve);
5138
+ }
5139
+ // post not OK, but so was pre -> OK
5140
+ Ok(())
5141
+ }
5142
+
5143
+ /// Check that balances meet the channel reserve requirements or violates them (below reserve).
5144
+ /// The channel value is an input as opposed to using from the FundingScope, so that this can be used in case of splicing
5145
+ /// to check with new channel value (before being committed to it).
5146
+ #[cfg(splicing)]
5147
+ 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> {
5148
+ let is_ok_self = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
5149
+ self_balance_pre, self_balance_post, channel_value_pre, channel_value_post,
5150
+ self.holder_dust_limit_satoshis
5151
+ );
5152
+ if let Err(channel_reserve_self) = is_ok_self {
5153
+ return Err(ChannelError::Warn(format!(
5154
+ "Balance below reserve, mandated by holder, {} vs {}",
5155
+ self_balance_post, channel_reserve_self,
5156
+ )));
5157
+ }
5158
+ let is_ok_cp = Self::check_splice_balance_meets_v2_reserve_requirement_noerr(
5159
+ counterparty_balance_pre, counterparty_balance_post, channel_value_pre, channel_value_post,
5160
+ self.counterparty_dust_limit_satoshis
5161
+ );
5162
+ if let Err(channel_reserve_cp) = is_ok_cp {
5163
+ return Err(ChannelError::Warn(format!(
5164
+ "Balance below reserve mandated by counterparty, {} vs {}",
5165
+ counterparty_balance_post, channel_reserve_cp,
5166
+ )));
5167
+ }
5168
+ Ok(())
5169
+ }
5170
+
5101
5171
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
5102
5172
/// number of pending HTLCs that are on track to be in our next commitment tx.
5103
5173
///
@@ -10334,14 +10404,28 @@ where
10334
10404
10335
10405
/// Handle splice_ack
10336
10406
#[cfg(splicing)]
10337
- pub fn splice_ack(&mut self, _msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
10407
+ pub fn splice_ack(&mut self, msg : &msgs::SpliceAck) -> Result<(), ChannelError> {
10338
10408
// check if splice is pending
10339
- if self.pending_splice.is_none() {
10409
+ let pending_splice = if let Some(pending_splice) = &self.pending_splice {
10410
+ pending_splice
10411
+ } else {
10340
10412
return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
10341
10413
};
10342
10414
10343
- // TODO(splicing): Pre-check for reserve requirement
10415
+ // Pre-check for reserve requirement
10344
10416
// (Note: It should also be checked later at tx_complete)
10417
+ let our_funding_contribution = pending_splice.our_funding_contribution;
10418
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
10419
+
10420
+ let pre_channel_value = self.funding.get_value_satoshis();
10421
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
10422
+ let pre_balance_self = self.funding.value_to_self_msat;
10423
+ let post_balance_self = PendingSplice::add_checked(pre_balance_self, our_funding_contribution);
10424
+ let pre_balance_counterparty = pre_channel_value.saturating_sub(pre_balance_self);
10425
+ let post_balance_counterparty = post_channel_value.saturating_sub(post_balance_self);
10426
+ // Pre-check for reserve requirement
10427
+ // This will also be checked later at tx_complete
10428
+ 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)?;
10345
10429
Ok(())
10346
10430
}
10347
10431
@@ -15106,4 +15190,69 @@ mod tests {
15106
15190
);
15107
15191
}
15108
15192
}
15193
+
15194
+ #[cfg(splicing)]
15195
+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
15196
+ use crate::ln::channel::PendingSplice;
15197
+
15198
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
15199
+ (pre_channel_value, post_channel_value)
15200
+ }
15201
+
15202
+ #[cfg(splicing)]
15203
+ #[test]
15204
+ fn test_splice_compute_post_value() {
15205
+ {
15206
+ // increase, small amounts
15207
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
15208
+ assert_eq!(pre_channel_value, 9_000);
15209
+ assert_eq!(post_channel_value, 15_000);
15210
+ }
15211
+ {
15212
+ // increase, small amounts
15213
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
15214
+ assert_eq!(pre_channel_value, 9_000);
15215
+ assert_eq!(post_channel_value, 15_000);
15216
+ }
15217
+ {
15218
+ // increase, small amounts
15219
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
15220
+ assert_eq!(pre_channel_value, 9_000);
15221
+ assert_eq!(post_channel_value, 15_000);
15222
+ }
15223
+ {
15224
+ // decrease, small amounts
15225
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
15226
+ assert_eq!(pre_channel_value, 15_000);
15227
+ assert_eq!(post_channel_value, 9_000);
15228
+ }
15229
+ {
15230
+ // decrease, small amounts
15231
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
15232
+ assert_eq!(pre_channel_value, 15_000);
15233
+ assert_eq!(post_channel_value, 9_000);
15234
+ }
15235
+ {
15236
+ // increase and decrease
15237
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
15238
+ assert_eq!(pre_channel_value, 15_000);
15239
+ assert_eq!(post_channel_value, 17_000);
15240
+ }
15241
+ let base2: u64 = 2;
15242
+ let huge63i3 = (base2.pow(63) - 3) as i64;
15243
+ assert_eq!(huge63i3, 9223372036854775805);
15244
+ assert_eq!(-huge63i3, -9223372036854775805);
15245
+ {
15246
+ // increase, large amount
15247
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
15248
+ assert_eq!(pre_channel_value, 9_000);
15249
+ assert_eq!(post_channel_value, 9223372036854784807);
15250
+ }
15251
+ {
15252
+ // increase, large amounts
15253
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
15254
+ assert_eq!(pre_channel_value, 9_000);
15255
+ assert_eq!(post_channel_value, 9223372036854784807);
15256
+ }
15257
+ }
15109
15258
}
0 commit comments