@@ -11,7 +11,6 @@ use bitcoin::amount::Amount;
11
11
use bitcoin::constants::ChainHash;
12
12
use bitcoin::script::{Script, ScriptBuf, Builder, WScriptHash};
13
13
use bitcoin::transaction::{Transaction, TxIn, TxOut};
14
- use bitcoin::sighash;
15
14
use bitcoin::sighash::EcdsaSighashType;
16
15
use bitcoin::consensus::encode;
17
16
use bitcoin::absolute::LockTime;
@@ -25,7 +24,7 @@ use bitcoin::hash_types::{Txid, BlockHash};
25
24
use bitcoin::secp256k1::constants::PUBLIC_KEY_SIZE;
26
25
use bitcoin::secp256k1::{PublicKey,SecretKey};
27
26
use bitcoin::secp256k1::{Secp256k1,ecdsa::Signature};
28
- use bitcoin::secp256k1;
27
+ use bitcoin::{ secp256k1, sighash} ;
29
28
30
29
use crate::ln::types::ChannelId;
31
30
use crate::types::payment::{PaymentPreimage, PaymentHash};
@@ -1187,6 +1186,30 @@ impl UnfundedChannelContext {
1187
1186
}
1188
1187
}
1189
1188
1189
+ /// Info about a pending splice, used in the pre-splice channel
1190
+ #[cfg(splicing)]
1191
+ #[derive(Clone)]
1192
+ struct PendingSpliceInfoPre {
1193
+ pub our_funding_contribution: i64,
1194
+ }
1195
+
1196
+ #[cfg(splicing)]
1197
+ impl PendingSpliceInfoPre {
1198
+ #[inline]
1199
+ fn add_checked(base: u64, delta: i64) -> u64 {
1200
+ if delta >= 0 {
1201
+ base.saturating_add(delta as u64)
1202
+ } else {
1203
+ base.saturating_sub(delta.abs() as u64)
1204
+ }
1205
+ }
1206
+
1207
+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1208
+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1209
+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1210
+ }
1211
+ }
1212
+
1190
1213
/// Contains everything about the channel including state, and various flags.
1191
1214
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1192
1215
config: LegacyChannelConfig,
@@ -1222,6 +1245,10 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1222
1245
secp_ctx: Secp256k1<secp256k1::All>,
1223
1246
channel_value_satoshis: u64,
1224
1247
1248
+ /// Info about an in-progress, pending splice (if any), on the pre-splice channel
1249
+ #[cfg(splicing)]
1250
+ pending_splice_pre: Option<PendingSpliceInfoPre>,
1251
+
1225
1252
latest_monitor_update_id: u64,
1226
1253
1227
1254
holder_signer: ChannelSignerType<SP>,
@@ -2343,6 +2370,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
2343
2370
is_manual_broadcast: false,
2344
2371
2345
2372
next_funding_txid: None,
2373
+
2374
+ #[cfg(splicing)]
2375
+ pending_splice_pre: None,
2346
2376
};
2347
2377
2348
2378
Ok(channel_context)
@@ -2573,6 +2603,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
2573
2603
local_initiated_shutdown: None,
2574
2604
is_manual_broadcast: false,
2575
2605
next_funding_txid: None,
2606
+
2607
+ #[cfg(splicing)]
2608
+ pending_splice_pre: None,
2576
2609
})
2577
2610
}
2578
2611
@@ -3755,6 +3788,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
3755
3788
(context.holder_selected_channel_reserve_satoshis, context.counterparty_selected_channel_reserve_satoshis)
3756
3789
}
3757
3790
3791
+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
3792
+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
3793
+ /// to checks with new channel value (before being comitted to it).
3794
+ #[cfg(splicing)]
3795
+ pub fn check_balance_meets_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
3796
+ if balance == 0 {
3797
+ return Ok(());
3798
+ }
3799
+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
3800
+ channel_value, self.holder_dust_limit_satoshis);
3801
+ if balance < holder_selected_channel_reserve_satoshis {
3802
+ return Err(ChannelError::Warn(format!(
3803
+ "Balance below reserve mandated by holder, {} vs {}",
3804
+ balance, holder_selected_channel_reserve_satoshis,
3805
+ )));
3806
+ }
3807
+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
3808
+ channel_value, self.counterparty_dust_limit_satoshis);
3809
+ if balance < counterparty_selected_channel_reserve_satoshis {
3810
+ return Err(ChannelError::Warn(format!(
3811
+ "Balance below reserve mandated by counterparty, {} vs {}",
3812
+ balance, counterparty_selected_channel_reserve_satoshis,
3813
+ )));
3814
+ }
3815
+ Ok(())
3816
+ }
3817
+
3758
3818
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
3759
3819
/// number of pending HTLCs that are on track to be in our next commitment tx.
3760
3820
///
@@ -4217,6 +4277,38 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4217
4277
self.channel_transaction_parameters = channel_transaction_parameters;
4218
4278
self.get_initial_counterparty_commitment_signature(logger)
4219
4279
}
4280
+
4281
+ /// Get the splice message that can be sent during splice initiation.
4282
+ #[cfg(splicing)]
4283
+ pub fn get_splice_init(&self, our_funding_contribution_satoshis: i64,
4284
+ funding_feerate_perkw: u32, locktime: u32,
4285
+ ) -> msgs::SpliceInit {
4286
+ // Reuse the existing funding pubkey, in spite of the channel value changing
4287
+ // (though at this point we don't know the new value yet, due tue the optional counterparty contribution)
4288
+ // Note that channel_keys_id is supposed NOT to change
4289
+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey.clone();
4290
+ msgs::SpliceInit {
4291
+ channel_id: self.channel_id,
4292
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4293
+ funding_feerate_perkw,
4294
+ locktime,
4295
+ funding_pubkey,
4296
+ require_confirmed_inputs: None,
4297
+ }
4298
+ }
4299
+
4300
+ /// Get the splice_ack message that can be sent in response to splice initiation.
4301
+ #[cfg(splicing)]
4302
+ pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
4303
+ // Reuse the existing funding pubkey, in spite of the channel value changing
4304
+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
4305
+ msgs::SpliceAck {
4306
+ channel_id: self.channel_id,
4307
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4308
+ funding_pubkey,
4309
+ require_confirmed_inputs: None,
4310
+ }
4311
+ }
4220
4312
}
4221
4313
4222
4314
// Internal utility functions for channels
@@ -7956,6 +8048,124 @@ impl<SP: Deref> Channel<SP> where
7956
8048
}
7957
8049
}
7958
8050
8051
+ /// Initiate splicing
8052
+ #[cfg(splicing)]
8053
+ pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
8054
+ funding_feerate_perkw: u32, locktime: u32,
8055
+ ) -> Result<msgs::SpliceInit, ChannelError> {
8056
+ // Check if a splice has been initiated already.
8057
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8058
+ if let Some(splice_info) = &self.context.pending_splice_pre {
8059
+ return Err(ChannelError::Warn(format!(
8060
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
8061
+ )));
8062
+ }
8063
+
8064
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8065
+ return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
8066
+ }
8067
+
8068
+ let pre_channel_value = self.context.get_value_satoshis();
8069
+ // Sanity check: capacity cannot decrease below 0
8070
+ if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
8071
+ return Err(ChannelError::Warn(format!(
8072
+ "Post-splicing channel value cannot be negative. It was {} + {}",
8073
+ pre_channel_value, our_funding_contribution_satoshis
8074
+ )));
8075
+ }
8076
+
8077
+ if our_funding_contribution_satoshis < 0 {
8078
+ return Err(ChannelError::Warn(format!(
8079
+ "TODO(splicing): Splice-out not supported, only splice in, contribution {}",
8080
+ our_funding_contribution_satoshis,
8081
+ )));
8082
+ }
8083
+
8084
+ // Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
8085
+ // (Cannot test for miminum required post-splice channel value)
8086
+
8087
+ self.context.pending_splice_pre = Some(PendingSpliceInfoPre {
8088
+ our_funding_contribution: our_funding_contribution_satoshis,
8089
+ });
8090
+
8091
+ let msg = self.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
8092
+ Ok(msg)
8093
+ }
8094
+
8095
+ /// Handle splice_init
8096
+ #[cfg(splicing)]
8097
+ pub fn splice_init(&mut self, msg: &msgs::SpliceInit) -> Result<msgs::SpliceAck, ChannelError> {
8098
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8099
+ // TODO(splicing): Currently not possible to contribute on the splicing-acceptor side
8100
+ let our_funding_contribution_satoshis = 0i64;
8101
+
8102
+ // Check if a splice has been initiated already.
8103
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8104
+ if let Some(splice_info) = &self.context.pending_splice_pre {
8105
+ return Err(ChannelError::Warn(format!(
8106
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
8107
+ )));
8108
+ }
8109
+
8110
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8111
+ return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
8112
+ }
8113
+
8114
+ let pre_channel_value = self.context.get_value_satoshis();
8115
+ // Sanity check: capacity cannot decrease below 0
8116
+ if (pre_channel_value as i64)
8117
+ .saturating_add(their_funding_contribution_satoshis)
8118
+ .saturating_add(our_funding_contribution_satoshis) < 0
8119
+ {
8120
+ return Err(ChannelError::Warn(format!(
8121
+ "Post-splicing channel value cannot be negative. It was {} + {} + {}",
8122
+ pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8123
+ )));
8124
+ }
8125
+
8126
+ if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
8127
+ return Err(ChannelError::Warn(format!(
8128
+ "Splice-out not supported, only splice in, relative {} + {}",
8129
+ their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8130
+ )));
8131
+ }
8132
+
8133
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
8134
+ let post_balance = PendingSpliceInfoPre::add_checked(self.context.value_to_self_msat, our_funding_contribution_satoshis);
8135
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8136
+ // This will also be checked later at tx_complete
8137
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8138
+
8139
+ // TODO(splicing): Store msg.funding_pubkey
8140
+ // TODO(splicing): Apply start of splice (splice_start)
8141
+
8142
+ let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis);
8143
+ // TODO(splicing): start interactive funding negotiation
8144
+ Ok(splice_ack_msg)
8145
+ }
8146
+
8147
+ /// Handle splice_ack
8148
+ #[cfg(splicing)]
8149
+ pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
8150
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8151
+
8152
+ // check if splice is pending
8153
+ let pending_splice = if let Some(pending_splice) = &self.context.pending_splice_pre {
8154
+ pending_splice
8155
+ } else {
8156
+ return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8157
+ };
8158
+
8159
+ let our_funding_contribution = pending_splice.our_funding_contribution;
8160
+
8161
+ let pre_channel_value = self.context.get_value_satoshis();
8162
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8163
+ let post_balance = PendingSpliceInfoPre::add_checked(self.context.value_to_self_msat, our_funding_contribution);
8164
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8165
+ // This will also be checked later at tx_complete
8166
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8167
+ Ok(())
8168
+ }
7959
8169
7960
8170
// Send stuff to our remote peers:
7961
8171
@@ -10373,6 +10583,9 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
10373
10583
// during a signing session, but have not received `tx_signatures` we MUST set `next_funding_txid`
10374
10584
// to the txid of that interactive transaction, else we MUST NOT set it.
10375
10585
next_funding_txid: None,
10586
+
10587
+ #[cfg(splicing)]
10588
+ pending_splice_pre: None,
10376
10589
},
10377
10590
interactive_tx_signing_session: None,
10378
10591
holder_commitment_point,
@@ -12158,4 +12371,69 @@ mod tests {
12158
12371
assert_eq!(node_a_chan.context.channel_state, ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::THEIR_CHANNEL_READY));
12159
12372
assert!(node_a_chan.check_get_channel_ready(0, &&logger).is_some());
12160
12373
}
12374
+
12375
+ #[cfg(all(test, splicing))]
12376
+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
12377
+ use crate::ln::channel::PendingSpliceInfoPre;
12378
+
12379
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
12380
+ (pre_channel_value, post_channel_value)
12381
+ }
12382
+
12383
+ #[cfg(all(test, splicing))]
12384
+ #[test]
12385
+ fn test_splice_compute_post_value() {
12386
+ {
12387
+ // increase, small amounts
12388
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
12389
+ assert_eq!(pre_channel_value, 9_000);
12390
+ assert_eq!(post_channel_value, 15_000);
12391
+ }
12392
+ {
12393
+ // increase, small amounts
12394
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
12395
+ assert_eq!(pre_channel_value, 9_000);
12396
+ assert_eq!(post_channel_value, 15_000);
12397
+ }
12398
+ {
12399
+ // increase, small amounts
12400
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
12401
+ assert_eq!(pre_channel_value, 9_000);
12402
+ assert_eq!(post_channel_value, 15_000);
12403
+ }
12404
+ {
12405
+ // decrease, small amounts
12406
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
12407
+ assert_eq!(pre_channel_value, 15_000);
12408
+ assert_eq!(post_channel_value, 9_000);
12409
+ }
12410
+ {
12411
+ // decrease, small amounts
12412
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
12413
+ assert_eq!(pre_channel_value, 15_000);
12414
+ assert_eq!(post_channel_value, 9_000);
12415
+ }
12416
+ {
12417
+ // increase and decrease
12418
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
12419
+ assert_eq!(pre_channel_value, 15_000);
12420
+ assert_eq!(post_channel_value, 17_000);
12421
+ }
12422
+ let base2: u64 = 2;
12423
+ let huge63i3 = (base2.pow(63) - 3) as i64;
12424
+ assert_eq!(huge63i3, 9223372036854775805);
12425
+ assert_eq!(-huge63i3, -9223372036854775805);
12426
+ {
12427
+ // increase, large amount
12428
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
12429
+ assert_eq!(pre_channel_value, 9_000);
12430
+ assert_eq!(post_channel_value, 9223372036854784807);
12431
+ }
12432
+ {
12433
+ // increase, large amounts
12434
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
12435
+ assert_eq!(pre_channel_value, 9_000);
12436
+ assert_eq!(post_channel_value, 9223372036854784807);
12437
+ }
12438
+ }
12161
12439
}
0 commit comments