@@ -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};
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};
@@ -1182,6 +1181,30 @@ impl UnfundedChannelContext {
1182
1181
}
1183
1182
}
1184
1183
1184
+ /// Info about a pending splice, used in the pre-splice channel
1185
+ #[cfg(splicing)]
1186
+ #[derive(Clone)]
1187
+ struct PendingSpliceInfoPre {
1188
+ pub our_funding_contribution: i64,
1189
+ }
1190
+
1191
+ #[cfg(splicing)]
1192
+ impl PendingSpliceInfoPre {
1193
+ #[inline]
1194
+ fn add_checked(base: u64, delta: i64) -> u64 {
1195
+ if delta >= 0 {
1196
+ base.saturating_add(delta as u64)
1197
+ } else {
1198
+ base.saturating_sub(delta.abs() as u64)
1199
+ }
1200
+ }
1201
+
1202
+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1203
+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1204
+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1205
+ }
1206
+ }
1207
+
1185
1208
/// Contains everything about the channel including state, and various flags.
1186
1209
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1187
1210
config: LegacyChannelConfig,
@@ -1217,6 +1240,10 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1217
1240
secp_ctx: Secp256k1<secp256k1::All>,
1218
1241
channel_value_satoshis: u64,
1219
1242
1243
+ /// Info about an in-progress, pending splice (if any), on the pre-splice channel
1244
+ #[cfg(splicing)]
1245
+ pending_splice_pre: Option<PendingSpliceInfoPre>,
1246
+
1220
1247
latest_monitor_update_id: u64,
1221
1248
1222
1249
holder_signer: ChannelSignerType<SP>,
@@ -2207,6 +2234,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
2207
2234
is_manual_broadcast: false,
2208
2235
2209
2236
next_funding_txid: None,
2237
+
2238
+ #[cfg(splicing)]
2239
+ pending_splice_pre: None,
2210
2240
};
2211
2241
2212
2242
Ok(channel_context)
@@ -2440,6 +2470,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
2440
2470
local_initiated_shutdown: None,
2441
2471
is_manual_broadcast: false,
2442
2472
next_funding_txid: None,
2473
+
2474
+ #[cfg(splicing)]
2475
+ pending_splice_pre: None,
2443
2476
})
2444
2477
}
2445
2478
@@ -3615,6 +3648,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
3615
3648
(context.holder_selected_channel_reserve_satoshis, context.counterparty_selected_channel_reserve_satoshis)
3616
3649
}
3617
3650
3651
+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
3652
+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
3653
+ /// to checks with new channel value (before being comitted to it).
3654
+ #[cfg(splicing)]
3655
+ pub fn check_balance_meets_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
3656
+ if balance == 0 {
3657
+ return Ok(());
3658
+ }
3659
+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
3660
+ channel_value, self.holder_dust_limit_satoshis);
3661
+ if balance < holder_selected_channel_reserve_satoshis {
3662
+ return Err(ChannelError::Warn(format!(
3663
+ "Balance below reserve mandated by holder, {} vs {}",
3664
+ balance, holder_selected_channel_reserve_satoshis,
3665
+ )));
3666
+ }
3667
+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
3668
+ channel_value, self.counterparty_dust_limit_satoshis);
3669
+ if balance < counterparty_selected_channel_reserve_satoshis {
3670
+ return Err(ChannelError::Warn(format!(
3671
+ "Balance below reserve mandated by counterparty, {} vs {}",
3672
+ balance, counterparty_selected_channel_reserve_satoshis,
3673
+ )));
3674
+ }
3675
+ Ok(())
3676
+ }
3677
+
3618
3678
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
3619
3679
/// number of pending HTLCs that are on track to be in our next commitment tx.
3620
3680
///
@@ -4085,6 +4145,38 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4085
4145
self.channel_transaction_parameters = channel_transaction_parameters;
4086
4146
self.get_initial_counterparty_commitment_signature(logger)
4087
4147
}
4148
+
4149
+ /// Get the splice message that can be sent during splice initiation.
4150
+ #[cfg(splicing)]
4151
+ pub fn get_splice_init(&self, our_funding_contribution_satoshis: i64,
4152
+ funding_feerate_perkw: u32, locktime: u32,
4153
+ ) -> msgs::SpliceInit {
4154
+ // Reuse the existing funding pubkey, in spite of the channel value changing
4155
+ // (though at this point we don't know the new value yet, due tue the optional counterparty contribution)
4156
+ // Note that channel_keys_id is supposed NOT to change
4157
+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey.clone();
4158
+ msgs::SpliceInit {
4159
+ channel_id: self.channel_id,
4160
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4161
+ funding_feerate_perkw,
4162
+ locktime,
4163
+ funding_pubkey,
4164
+ require_confirmed_inputs: None,
4165
+ }
4166
+ }
4167
+
4168
+ /// Get the splice_ack message that can be sent in response to splice initiation.
4169
+ #[cfg(splicing)]
4170
+ pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
4171
+ // Reuse the existing funding pubkey, in spite of the channel value changing
4172
+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
4173
+ msgs::SpliceAck {
4174
+ channel_id: self.channel_id,
4175
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4176
+ funding_pubkey,
4177
+ require_confirmed_inputs: None,
4178
+ }
4179
+ }
4088
4180
}
4089
4181
4090
4182
// Internal utility functions for channels
@@ -7807,6 +7899,124 @@ impl<SP: Deref> Channel<SP> where
7807
7899
}
7808
7900
}
7809
7901
7902
+ /// Initiate splicing
7903
+ #[cfg(splicing)]
7904
+ pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
7905
+ funding_feerate_perkw: u32, locktime: u32,
7906
+ ) -> Result<msgs::SpliceInit, ChannelError> {
7907
+ // Check if a splice has been initiated already.
7908
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
7909
+ if let Some(splice_info) = &self.context.pending_splice_pre {
7910
+ return Err(ChannelError::Warn(format!(
7911
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
7912
+ )));
7913
+ }
7914
+
7915
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
7916
+ return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
7917
+ }
7918
+
7919
+ let pre_channel_value = self.context.get_value_satoshis();
7920
+ // Sanity check: capacity cannot decrease below 0
7921
+ if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
7922
+ return Err(ChannelError::Warn(format!(
7923
+ "Post-splicing channel value cannot be negative. It was {} + {}",
7924
+ pre_channel_value, our_funding_contribution_satoshis
7925
+ )));
7926
+ }
7927
+
7928
+ if our_funding_contribution_satoshis < 0 {
7929
+ return Err(ChannelError::Warn(format!(
7930
+ "TODO(splicing): Splice-out not supported, only splice in, contribution {}",
7931
+ our_funding_contribution_satoshis,
7932
+ )));
7933
+ }
7934
+
7935
+ // Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
7936
+ // (Cannot test for miminum required post-splice channel value)
7937
+
7938
+ self.context.pending_splice_pre = Some(PendingSpliceInfoPre {
7939
+ our_funding_contribution: our_funding_contribution_satoshis,
7940
+ });
7941
+
7942
+ let msg = self.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
7943
+ Ok(msg)
7944
+ }
7945
+
7946
+ /// Handle splice_init
7947
+ #[cfg(splicing)]
7948
+ pub fn splice_init(&mut self, msg: &msgs::SpliceInit) -> Result<msgs::SpliceAck, ChannelError> {
7949
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
7950
+ // TODO(splicing): Currently not possible to contribute on the splicing-acceptor side
7951
+ let our_funding_contribution_satoshis = 0i64;
7952
+
7953
+ // Check if a splice has been initiated already.
7954
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
7955
+ if let Some(splice_info) = &self.context.pending_splice_pre {
7956
+ return Err(ChannelError::Warn(format!(
7957
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
7958
+ )));
7959
+ }
7960
+
7961
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
7962
+ return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
7963
+ }
7964
+
7965
+ let pre_channel_value = self.context.get_value_satoshis();
7966
+ // Sanity check: capacity cannot decrease below 0
7967
+ if (pre_channel_value as i64)
7968
+ .saturating_add(their_funding_contribution_satoshis)
7969
+ .saturating_add(our_funding_contribution_satoshis) < 0
7970
+ {
7971
+ return Err(ChannelError::Warn(format!(
7972
+ "Post-splicing channel value cannot be negative. It was {} + {} + {}",
7973
+ pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
7974
+ )));
7975
+ }
7976
+
7977
+ if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
7978
+ return Err(ChannelError::Warn(format!(
7979
+ "Splice-out not supported, only splice in, relative {} + {}",
7980
+ their_funding_contribution_satoshis, our_funding_contribution_satoshis,
7981
+ )));
7982
+ }
7983
+
7984
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
7985
+ let post_balance = PendingSpliceInfoPre::add_checked(self.context.value_to_self_msat, our_funding_contribution_satoshis);
7986
+ // Early check for reserve requirement, assuming maximum balance of full channel value
7987
+ // This will also be checked later at tx_complete
7988
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
7989
+
7990
+ // TODO(splicing): Store msg.funding_pubkey
7991
+ // TODO(splicing): Apply start of splice (splice_start)
7992
+
7993
+ let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis);
7994
+ // TODO(splicing): start interactive funding negotiation
7995
+ Ok(splice_ack_msg)
7996
+ }
7997
+
7998
+ /// Handle splice_ack
7999
+ #[cfg(splicing)]
8000
+ pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
8001
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8002
+
8003
+ // check if splice is pending
8004
+ let pending_splice = if let Some(pending_splice) = &self.context.pending_splice_pre {
8005
+ pending_splice
8006
+ } else {
8007
+ return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8008
+ };
8009
+
8010
+ let our_funding_contribution = pending_splice.our_funding_contribution;
8011
+
8012
+ let pre_channel_value = self.context.get_value_satoshis();
8013
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8014
+ let post_balance = PendingSpliceInfoPre::add_checked(self.context.value_to_self_msat, our_funding_contribution);
8015
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8016
+ // This will also be checked later at tx_complete
8017
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8018
+ Ok(())
8019
+ }
7810
8020
7811
8021
// Send stuff to our remote peers:
7812
8022
@@ -10123,6 +10333,9 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
10123
10333
// during a signing session, but have not received `tx_signatures` we MUST set `next_funding_txid`
10124
10334
// to the txid of that interactive transaction, else we MUST NOT set it.
10125
10335
next_funding_txid,
10336
+
10337
+ #[cfg(splicing)]
10338
+ pending_splice_pre: None,
10126
10339
},
10127
10340
interactive_tx_signing_session: None,
10128
10341
})
@@ -11907,4 +12120,69 @@ mod tests {
11907
12120
assert_eq!(node_a_chan.context.channel_state, ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::THEIR_CHANNEL_READY));
11908
12121
assert!(node_a_chan.check_get_channel_ready(0, &&logger).is_some());
11909
12122
}
12123
+
12124
+ #[cfg(all(test, splicing))]
12125
+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
12126
+ use crate::ln::channel::PendingSpliceInfoPre;
12127
+
12128
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
12129
+ (pre_channel_value, post_channel_value)
12130
+ }
12131
+
12132
+ #[cfg(all(test, splicing))]
12133
+ #[test]
12134
+ fn test_splice_compute_post_value() {
12135
+ {
12136
+ // increase, small amounts
12137
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
12138
+ assert_eq!(pre_channel_value, 9_000);
12139
+ assert_eq!(post_channel_value, 15_000);
12140
+ }
12141
+ {
12142
+ // increase, small amounts
12143
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
12144
+ assert_eq!(pre_channel_value, 9_000);
12145
+ assert_eq!(post_channel_value, 15_000);
12146
+ }
12147
+ {
12148
+ // increase, small amounts
12149
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
12150
+ assert_eq!(pre_channel_value, 9_000);
12151
+ assert_eq!(post_channel_value, 15_000);
12152
+ }
12153
+ {
12154
+ // decrease, small amounts
12155
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
12156
+ assert_eq!(pre_channel_value, 15_000);
12157
+ assert_eq!(post_channel_value, 9_000);
12158
+ }
12159
+ {
12160
+ // decrease, small amounts
12161
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
12162
+ assert_eq!(pre_channel_value, 15_000);
12163
+ assert_eq!(post_channel_value, 9_000);
12164
+ }
12165
+ {
12166
+ // increase and decrease
12167
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
12168
+ assert_eq!(pre_channel_value, 15_000);
12169
+ assert_eq!(post_channel_value, 17_000);
12170
+ }
12171
+ let base2: u64 = 2;
12172
+ let huge63i3 = (base2.pow(63) - 3) as i64;
12173
+ assert_eq!(huge63i3, 9223372036854775805);
12174
+ assert_eq!(-huge63i3, -9223372036854775805);
12175
+ {
12176
+ // increase, large amount
12177
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
12178
+ assert_eq!(pre_channel_value, 9_000);
12179
+ assert_eq!(post_channel_value, 9223372036854784807);
12180
+ }
12181
+ {
12182
+ // increase, large amounts
12183
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
12184
+ assert_eq!(pre_channel_value, 9_000);
12185
+ assert_eq!(post_channel_value, 9223372036854784807);
12186
+ }
12187
+ }
11910
12188
}
0 commit comments