@@ -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};
@@ -1183,6 +1182,30 @@ impl UnfundedChannelContext {
1183
1182
}
1184
1183
}
1185
1184
1185
+ /// Info about a pending splice, used in the pre-splice channel
1186
+ #[cfg(splicing)]
1187
+ #[derive(Clone)]
1188
+ struct PendingSpliceInfoPre {
1189
+ pub our_funding_contribution: i64,
1190
+ }
1191
+
1192
+ #[cfg(splicing)]
1193
+ impl PendingSpliceInfoPre {
1194
+ #[inline]
1195
+ fn add_checked(base: u64, delta: i64) -> u64 {
1196
+ if delta >= 0 {
1197
+ base.saturating_add(delta as u64)
1198
+ } else {
1199
+ base.saturating_sub(delta.abs() as u64)
1200
+ }
1201
+ }
1202
+
1203
+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1204
+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1205
+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1206
+ }
1207
+ }
1208
+
1186
1209
/// Contains everything about the channel including state, and various flags.
1187
1210
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1188
1211
config: LegacyChannelConfig,
@@ -1218,6 +1241,10 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1218
1241
secp_ctx: Secp256k1<secp256k1::All>,
1219
1242
channel_value_satoshis: u64,
1220
1243
1244
+ /// Info about an in-progress, pending splice (if any), on the pre-splice channel
1245
+ #[cfg(splicing)]
1246
+ pending_splice_pre: Option<PendingSpliceInfoPre>,
1247
+
1221
1248
latest_monitor_update_id: u64,
1222
1249
1223
1250
holder_signer: ChannelSignerType<SP>,
@@ -2328,6 +2355,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
2328
2355
is_manual_broadcast: false,
2329
2356
2330
2357
next_funding_txid: None,
2358
+
2359
+ #[cfg(splicing)]
2360
+ pending_splice_pre: None,
2331
2361
};
2332
2362
2333
2363
Ok(channel_context)
@@ -2561,6 +2591,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
2561
2591
local_initiated_shutdown: None,
2562
2592
is_manual_broadcast: false,
2563
2593
next_funding_txid: None,
2594
+
2595
+ #[cfg(splicing)]
2596
+ pending_splice_pre: None,
2564
2597
})
2565
2598
}
2566
2599
@@ -3744,6 +3777,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
3744
3777
(context.holder_selected_channel_reserve_satoshis, context.counterparty_selected_channel_reserve_satoshis)
3745
3778
}
3746
3779
3780
+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
3781
+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
3782
+ /// to checks with new channel value (before being comitted to it).
3783
+ #[cfg(splicing)]
3784
+ pub fn check_balance_meets_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
3785
+ if balance == 0 {
3786
+ return Ok(());
3787
+ }
3788
+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
3789
+ channel_value, self.holder_dust_limit_satoshis);
3790
+ if balance < holder_selected_channel_reserve_satoshis {
3791
+ return Err(ChannelError::Warn(format!(
3792
+ "Balance below reserve mandated by holder, {} vs {}",
3793
+ balance, holder_selected_channel_reserve_satoshis,
3794
+ )));
3795
+ }
3796
+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
3797
+ channel_value, self.counterparty_dust_limit_satoshis);
3798
+ if balance < counterparty_selected_channel_reserve_satoshis {
3799
+ return Err(ChannelError::Warn(format!(
3800
+ "Balance below reserve mandated by counterparty, {} vs {}",
3801
+ balance, counterparty_selected_channel_reserve_satoshis,
3802
+ )));
3803
+ }
3804
+ Ok(())
3805
+ }
3806
+
3747
3807
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
3748
3808
/// number of pending HTLCs that are on track to be in our next commitment tx.
3749
3809
///
@@ -4214,6 +4274,38 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4214
4274
self.channel_transaction_parameters = channel_transaction_parameters;
4215
4275
self.get_initial_counterparty_commitment_signature(logger)
4216
4276
}
4277
+
4278
+ /// Get the splice message that can be sent during splice initiation.
4279
+ #[cfg(splicing)]
4280
+ pub fn get_splice_init(&self, our_funding_contribution_satoshis: i64,
4281
+ funding_feerate_perkw: u32, locktime: u32,
4282
+ ) -> msgs::SpliceInit {
4283
+ // Reuse the existing funding pubkey, in spite of the channel value changing
4284
+ // (though at this point we don't know the new value yet, due tue the optional counterparty contribution)
4285
+ // Note that channel_keys_id is supposed NOT to change
4286
+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey.clone();
4287
+ msgs::SpliceInit {
4288
+ channel_id: self.channel_id,
4289
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4290
+ funding_feerate_perkw,
4291
+ locktime,
4292
+ funding_pubkey,
4293
+ require_confirmed_inputs: None,
4294
+ }
4295
+ }
4296
+
4297
+ /// Get the splice_ack message that can be sent in response to splice initiation.
4298
+ #[cfg(splicing)]
4299
+ pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
4300
+ // Reuse the existing funding pubkey, in spite of the channel value changing
4301
+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
4302
+ msgs::SpliceAck {
4303
+ channel_id: self.channel_id,
4304
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4305
+ funding_pubkey,
4306
+ require_confirmed_inputs: None,
4307
+ }
4308
+ }
4217
4309
}
4218
4310
4219
4311
// Internal utility functions for channels
@@ -7941,6 +8033,124 @@ impl<SP: Deref> Channel<SP> where
7941
8033
}
7942
8034
}
7943
8035
8036
+ /// Initiate splicing
8037
+ #[cfg(splicing)]
8038
+ pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
8039
+ funding_feerate_perkw: u32, locktime: u32,
8040
+ ) -> Result<msgs::SpliceInit, ChannelError> {
8041
+ // Check if a splice has been initiated already.
8042
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8043
+ if let Some(splice_info) = &self.context.pending_splice_pre {
8044
+ return Err(ChannelError::Warn(format!(
8045
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
8046
+ )));
8047
+ }
8048
+
8049
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8050
+ return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
8051
+ }
8052
+
8053
+ let pre_channel_value = self.context.get_value_satoshis();
8054
+ // Sanity check: capacity cannot decrease below 0
8055
+ if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
8056
+ return Err(ChannelError::Warn(format!(
8057
+ "Post-splicing channel value cannot be negative. It was {} + {}",
8058
+ pre_channel_value, our_funding_contribution_satoshis
8059
+ )));
8060
+ }
8061
+
8062
+ if our_funding_contribution_satoshis < 0 {
8063
+ return Err(ChannelError::Warn(format!(
8064
+ "TODO(splicing): Splice-out not supported, only splice in, contribution {}",
8065
+ our_funding_contribution_satoshis,
8066
+ )));
8067
+ }
8068
+
8069
+ // Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
8070
+ // (Cannot test for miminum required post-splice channel value)
8071
+
8072
+ self.context.pending_splice_pre = Some(PendingSpliceInfoPre {
8073
+ our_funding_contribution: our_funding_contribution_satoshis,
8074
+ });
8075
+
8076
+ let msg = self.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
8077
+ Ok(msg)
8078
+ }
8079
+
8080
+ /// Handle splice_init
8081
+ #[cfg(splicing)]
8082
+ pub fn splice_init(&mut self, msg: &msgs::SpliceInit) -> Result<msgs::SpliceAck, ChannelError> {
8083
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8084
+ // TODO(splicing): Currently not possible to contribute on the splicing-acceptor side
8085
+ let our_funding_contribution_satoshis = 0i64;
8086
+
8087
+ // Check if a splice has been initiated already.
8088
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8089
+ if let Some(splice_info) = &self.context.pending_splice_pre {
8090
+ return Err(ChannelError::Warn(format!(
8091
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
8092
+ )));
8093
+ }
8094
+
8095
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8096
+ return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
8097
+ }
8098
+
8099
+ let pre_channel_value = self.context.get_value_satoshis();
8100
+ // Sanity check: capacity cannot decrease below 0
8101
+ if (pre_channel_value as i64)
8102
+ .saturating_add(their_funding_contribution_satoshis)
8103
+ .saturating_add(our_funding_contribution_satoshis) < 0
8104
+ {
8105
+ return Err(ChannelError::Warn(format!(
8106
+ "Post-splicing channel value cannot be negative. It was {} + {} + {}",
8107
+ pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8108
+ )));
8109
+ }
8110
+
8111
+ if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
8112
+ return Err(ChannelError::Warn(format!(
8113
+ "Splice-out not supported, only splice in, relative {} + {}",
8114
+ their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8115
+ )));
8116
+ }
8117
+
8118
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
8119
+ let post_balance = PendingSpliceInfoPre::add_checked(self.context.value_to_self_msat, our_funding_contribution_satoshis);
8120
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8121
+ // This will also be checked later at tx_complete
8122
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8123
+
8124
+ // TODO(splicing): Store msg.funding_pubkey
8125
+ // TODO(splicing): Apply start of splice (splice_start)
8126
+
8127
+ let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis);
8128
+ // TODO(splicing): start interactive funding negotiation
8129
+ Ok(splice_ack_msg)
8130
+ }
8131
+
8132
+ /// Handle splice_ack
8133
+ #[cfg(splicing)]
8134
+ pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
8135
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8136
+
8137
+ // check if splice is pending
8138
+ let pending_splice = if let Some(pending_splice) = &self.context.pending_splice_pre {
8139
+ pending_splice
8140
+ } else {
8141
+ return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8142
+ };
8143
+
8144
+ let our_funding_contribution = pending_splice.our_funding_contribution;
8145
+
8146
+ let pre_channel_value = self.context.get_value_satoshis();
8147
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8148
+ let post_balance = PendingSpliceInfoPre::add_checked(self.context.value_to_self_msat, our_funding_contribution);
8149
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8150
+ // This will also be checked later at tx_complete
8151
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8152
+ Ok(())
8153
+ }
7944
8154
7945
8155
// Send stuff to our remote peers:
7946
8156
@@ -10259,6 +10469,9 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
10259
10469
// during a signing session, but have not received `tx_signatures` we MUST set `next_funding_txid`
10260
10470
// to the txid of that interactive transaction, else we MUST NOT set it.
10261
10471
next_funding_txid: None,
10472
+
10473
+ #[cfg(splicing)]
10474
+ pending_splice_pre: None,
10262
10475
},
10263
10476
interactive_tx_signing_session: None,
10264
10477
})
@@ -12043,4 +12256,69 @@ mod tests {
12043
12256
assert_eq!(node_a_chan.context.channel_state, ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::THEIR_CHANNEL_READY));
12044
12257
assert!(node_a_chan.check_get_channel_ready(0, &&logger).is_some());
12045
12258
}
12259
+
12260
+ #[cfg(all(test, splicing))]
12261
+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
12262
+ use crate::ln::channel::PendingSpliceInfoPre;
12263
+
12264
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
12265
+ (pre_channel_value, post_channel_value)
12266
+ }
12267
+
12268
+ #[cfg(all(test, splicing))]
12269
+ #[test]
12270
+ fn test_splice_compute_post_value() {
12271
+ {
12272
+ // increase, small amounts
12273
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
12274
+ assert_eq!(pre_channel_value, 9_000);
12275
+ assert_eq!(post_channel_value, 15_000);
12276
+ }
12277
+ {
12278
+ // increase, small amounts
12279
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
12280
+ assert_eq!(pre_channel_value, 9_000);
12281
+ assert_eq!(post_channel_value, 15_000);
12282
+ }
12283
+ {
12284
+ // increase, small amounts
12285
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
12286
+ assert_eq!(pre_channel_value, 9_000);
12287
+ assert_eq!(post_channel_value, 15_000);
12288
+ }
12289
+ {
12290
+ // decrease, small amounts
12291
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
12292
+ assert_eq!(pre_channel_value, 15_000);
12293
+ assert_eq!(post_channel_value, 9_000);
12294
+ }
12295
+ {
12296
+ // decrease, small amounts
12297
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
12298
+ assert_eq!(pre_channel_value, 15_000);
12299
+ assert_eq!(post_channel_value, 9_000);
12300
+ }
12301
+ {
12302
+ // increase and decrease
12303
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
12304
+ assert_eq!(pre_channel_value, 15_000);
12305
+ assert_eq!(post_channel_value, 17_000);
12306
+ }
12307
+ let base2: u64 = 2;
12308
+ let huge63i3 = (base2.pow(63) - 3) as i64;
12309
+ assert_eq!(huge63i3, 9223372036854775805);
12310
+ assert_eq!(-huge63i3, -9223372036854775805);
12311
+ {
12312
+ // increase, large amount
12313
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
12314
+ assert_eq!(pre_channel_value, 9_000);
12315
+ assert_eq!(post_channel_value, 9223372036854784807);
12316
+ }
12317
+ {
12318
+ // increase, large amounts
12319
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
12320
+ assert_eq!(pre_channel_value, 9_000);
12321
+ assert_eq!(post_channel_value, 9223372036854784807);
12322
+ }
12323
+ }
12046
12324
}
0 commit comments