@@ -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 PendingSplice {
1188
+ pub our_funding_contribution: i64,
1189
+ }
1190
+
1191
+ #[cfg(splicing)]
1192
+ impl PendingSplice {
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,
@@ -3623,6 +3646,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
3623
3646
(context.holder_selected_channel_reserve_satoshis, context.counterparty_selected_channel_reserve_satoshis)
3624
3647
}
3625
3648
3649
+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
3650
+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
3651
+ /// to checks with new channel value (before being comitted to it).
3652
+ #[cfg(splicing)]
3653
+ pub fn check_balance_meets_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
3654
+ if balance == 0 {
3655
+ return Ok(());
3656
+ }
3657
+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
3658
+ channel_value, self.holder_dust_limit_satoshis);
3659
+ if balance < holder_selected_channel_reserve_satoshis {
3660
+ return Err(ChannelError::Warn(format!(
3661
+ "Balance below reserve mandated by holder, {} vs {}",
3662
+ balance, holder_selected_channel_reserve_satoshis,
3663
+ )));
3664
+ }
3665
+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
3666
+ channel_value, self.counterparty_dust_limit_satoshis);
3667
+ if balance < counterparty_selected_channel_reserve_satoshis {
3668
+ return Err(ChannelError::Warn(format!(
3669
+ "Balance below reserve mandated by counterparty, {} vs {}",
3670
+ balance, counterparty_selected_channel_reserve_satoshis,
3671
+ )));
3672
+ }
3673
+ Ok(())
3674
+ }
3675
+
3626
3676
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
3627
3677
/// number of pending HTLCs that are on track to be in our next commitment tx.
3628
3678
///
@@ -4093,6 +4143,38 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4093
4143
self.channel_transaction_parameters = channel_transaction_parameters;
4094
4144
self.get_initial_counterparty_commitment_signature(logger)
4095
4145
}
4146
+
4147
+ /// Get the splice message that can be sent during splice initiation.
4148
+ #[cfg(splicing)]
4149
+ pub fn get_splice_init(&self, our_funding_contribution_satoshis: i64,
4150
+ funding_feerate_perkw: u32, locktime: u32,
4151
+ ) -> msgs::SpliceInit {
4152
+ // Reuse the existing funding pubkey, in spite of the channel value changing
4153
+ // (though at this point we don't know the new value yet, due tue the optional counterparty contribution)
4154
+ // Note that channel_keys_id is supposed NOT to change
4155
+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey.clone();
4156
+ msgs::SpliceInit {
4157
+ channel_id: self.channel_id,
4158
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4159
+ funding_feerate_perkw,
4160
+ locktime,
4161
+ funding_pubkey,
4162
+ require_confirmed_inputs: None,
4163
+ }
4164
+ }
4165
+
4166
+ /// Get the splice_ack message that can be sent in response to splice initiation.
4167
+ #[cfg(splicing)]
4168
+ pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
4169
+ // Reuse the existing funding pubkey, in spite of the channel value changing
4170
+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
4171
+ msgs::SpliceAck {
4172
+ channel_id: self.channel_id,
4173
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4174
+ funding_pubkey,
4175
+ require_confirmed_inputs: None,
4176
+ }
4177
+ }
4096
4178
}
4097
4179
4098
4180
// Internal utility functions for channels
@@ -4214,6 +4296,9 @@ pub(super) struct DualFundingChannelContext {
4214
4296
pub(super) struct Channel<SP: Deref> where SP::Target: SignerProvider {
4215
4297
pub context: ChannelContext<SP>,
4216
4298
pub interactive_tx_signing_session: Option<InteractiveTxSigningSession>,
4299
+ /// Info about an in-progress, pending splice (if any), on the pre-splice channel
4300
+ #[cfg(splicing)]
4301
+ pending_splice_pre: Option<PendingSplice>,
4217
4302
}
4218
4303
4219
4304
#[cfg(any(test, fuzzing))]
@@ -7815,6 +7900,135 @@ impl<SP: Deref> Channel<SP> where
7815
7900
}
7816
7901
}
7817
7902
7903
+ /// Initiate splicing
7904
+ #[cfg(splicing)]
7905
+ pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
7906
+ our_funding_inputs: Vec<(TxIn, Transaction)>, funding_feerate_perkw: u32, locktime: u32,
7907
+ ) -> Result<msgs::SpliceInit, ChannelError> {
7908
+ // Check if a splice has been initiated already.
7909
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
7910
+ if let Some(splice_info) = &self.pending_splice_pre {
7911
+ return Err(ChannelError::Warn(format!(
7912
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
7913
+ )));
7914
+ }
7915
+
7916
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
7917
+ return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
7918
+ }
7919
+
7920
+ let pre_channel_value = self.context.get_value_satoshis();
7921
+ // Sanity check: capacity cannot decrease below 0
7922
+ if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
7923
+ return Err(ChannelError::Warn(format!(
7924
+ "Post-splicing channel value cannot be negative. It was {} + {}",
7925
+ pre_channel_value, our_funding_contribution_satoshis
7926
+ )));
7927
+ }
7928
+
7929
+ if our_funding_contribution_satoshis < 0 {
7930
+ return Err(ChannelError::Warn(format!(
7931
+ "TODO(splicing): Splice-out not supported, only splice in, contribution {}",
7932
+ our_funding_contribution_satoshis,
7933
+ )));
7934
+ }
7935
+
7936
+ // Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
7937
+ // (Cannot test for miminum required post-splice channel value)
7938
+
7939
+ // Check that inputs are sufficient to cover our contribution
7940
+ let sum_input: i64 = our_funding_inputs.into_iter().map(
7941
+ |(txin, tx)| tx.output.get(txin.previous_output.vout as usize).map(|tx| tx.value.to_sat() as i64).unwrap_or(0)
7942
+ ).sum();
7943
+ if sum_input < our_funding_contribution_satoshis {
7944
+ return Err(ChannelError::Warn(format!(
7945
+ "Provided inputs are insufficient for our contribution, {} {}",
7946
+ sum_input, our_funding_contribution_satoshis,
7947
+ )));
7948
+ }
7949
+
7950
+ self.pending_splice_pre = Some(PendingSplice {
7951
+ our_funding_contribution: our_funding_contribution_satoshis,
7952
+ });
7953
+
7954
+ let msg = self.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
7955
+ Ok(msg)
7956
+ }
7957
+
7958
+ /// Handle splice_init
7959
+ #[cfg(splicing)]
7960
+ pub fn splice_init(&mut self, msg: &msgs::SpliceInit) -> Result<msgs::SpliceAck, ChannelError> {
7961
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
7962
+ // TODO(splicing): Currently not possible to contribute on the splicing-acceptor side
7963
+ let our_funding_contribution_satoshis = 0i64;
7964
+
7965
+ // Check if a splice has been initiated already.
7966
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
7967
+ if let Some(splice_info) = &self.pending_splice_pre {
7968
+ return Err(ChannelError::Warn(format!(
7969
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
7970
+ )));
7971
+ }
7972
+
7973
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
7974
+ return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
7975
+ }
7976
+
7977
+ let pre_channel_value = self.context.get_value_satoshis();
7978
+ // Sanity check: capacity cannot decrease below 0
7979
+ if (pre_channel_value as i64)
7980
+ .saturating_add(their_funding_contribution_satoshis)
7981
+ .saturating_add(our_funding_contribution_satoshis) < 0
7982
+ {
7983
+ return Err(ChannelError::Warn(format!(
7984
+ "Post-splicing channel value cannot be negative. It was {} + {} + {}",
7985
+ pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
7986
+ )));
7987
+ }
7988
+
7989
+ if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
7990
+ return Err(ChannelError::Warn(format!(
7991
+ "Splice-out not supported, only splice in, relative {} + {}",
7992
+ their_funding_contribution_satoshis, our_funding_contribution_satoshis,
7993
+ )));
7994
+ }
7995
+
7996
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
7997
+ let post_balance = PendingSplice::add_checked(self.context.value_to_self_msat, our_funding_contribution_satoshis);
7998
+ // Early check for reserve requirement, assuming maximum balance of full channel value
7999
+ // This will also be checked later at tx_complete
8000
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8001
+
8002
+ // TODO(splicing): Store msg.funding_pubkey
8003
+ // TODO(splicing): Apply start of splice (splice_start)
8004
+
8005
+ let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis);
8006
+ // TODO(splicing): start interactive funding negotiation
8007
+ Ok(splice_ack_msg)
8008
+ }
8009
+
8010
+ /// Handle splice_ack
8011
+ #[cfg(splicing)]
8012
+ pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
8013
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8014
+
8015
+ // check if splice is pending
8016
+ let pending_splice = if let Some(pending_splice) = &self.pending_splice_pre {
8017
+ pending_splice
8018
+ } else {
8019
+ return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8020
+ };
8021
+
8022
+ let our_funding_contribution = pending_splice.our_funding_contribution;
8023
+
8024
+ let pre_channel_value = self.context.get_value_satoshis();
8025
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8026
+ let post_balance = PendingSplice::add_checked(self.context.value_to_self_msat, our_funding_contribution);
8027
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8028
+ // This will also be checked later at tx_complete
8029
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8030
+ Ok(())
8031
+ }
7818
8032
7819
8033
// Send stuff to our remote peers:
7820
8034
@@ -8491,6 +8705,8 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
8491
8705
let mut channel = Channel {
8492
8706
context: self.context,
8493
8707
interactive_tx_signing_session: None,
8708
+ #[cfg(splicing)]
8709
+ pending_splice_pre: None,
8494
8710
};
8495
8711
8496
8712
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some();
@@ -8716,6 +8932,8 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
8716
8932
let mut channel = Channel {
8717
8933
context: self.context,
8718
8934
interactive_tx_signing_session: None,
8935
+ #[cfg(splicing)]
8936
+ pending_splice_pre: None,
8719
8937
};
8720
8938
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some();
8721
8939
channel.monitor_updating_paused(false, false, need_channel_ready, Vec::new(), Vec::new(), Vec::new());
@@ -8861,6 +9079,8 @@ impl<SP: Deref> OutboundV2Channel<SP> where SP::Target: SignerProvider {
8861
9079
let channel = Channel {
8862
9080
context: self.context,
8863
9081
interactive_tx_signing_session: Some(signing_session),
9082
+ #[cfg(splicing)]
9083
+ pending_splice_pre: None,
8864
9084
};
8865
9085
8866
9086
Ok(channel)
@@ -9055,6 +9275,8 @@ impl<SP: Deref> InboundV2Channel<SP> where SP::Target: SignerProvider {
9055
9275
let channel = Channel {
9056
9276
context: self.context,
9057
9277
interactive_tx_signing_session: Some(signing_session),
9278
+ #[cfg(splicing)]
9279
+ pending_splice_pre: None,
9058
9280
};
9059
9281
9060
9282
Ok(channel)
@@ -10133,6 +10355,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
10133
10355
next_funding_txid: None,
10134
10356
},
10135
10357
interactive_tx_signing_session: None,
10358
+ #[cfg(splicing)]
10359
+ pending_splice_pre: None,
10136
10360
})
10137
10361
}
10138
10362
}
@@ -11915,4 +12139,69 @@ mod tests {
11915
12139
assert_eq!(node_a_chan.context.channel_state, ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::THEIR_CHANNEL_READY));
11916
12140
assert!(node_a_chan.check_get_channel_ready(0, &&logger).is_some());
11917
12141
}
12142
+
12143
+ #[cfg(all(test, splicing))]
12144
+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
12145
+ use crate::ln::channel::PendingSplice;
12146
+
12147
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
12148
+ (pre_channel_value, post_channel_value)
12149
+ }
12150
+
12151
+ #[cfg(all(test, splicing))]
12152
+ #[test]
12153
+ fn test_splice_compute_post_value() {
12154
+ {
12155
+ // increase, small amounts
12156
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
12157
+ assert_eq!(pre_channel_value, 9_000);
12158
+ assert_eq!(post_channel_value, 15_000);
12159
+ }
12160
+ {
12161
+ // increase, small amounts
12162
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
12163
+ assert_eq!(pre_channel_value, 9_000);
12164
+ assert_eq!(post_channel_value, 15_000);
12165
+ }
12166
+ {
12167
+ // increase, small amounts
12168
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
12169
+ assert_eq!(pre_channel_value, 9_000);
12170
+ assert_eq!(post_channel_value, 15_000);
12171
+ }
12172
+ {
12173
+ // decrease, small amounts
12174
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
12175
+ assert_eq!(pre_channel_value, 15_000);
12176
+ assert_eq!(post_channel_value, 9_000);
12177
+ }
12178
+ {
12179
+ // decrease, small amounts
12180
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
12181
+ assert_eq!(pre_channel_value, 15_000);
12182
+ assert_eq!(post_channel_value, 9_000);
12183
+ }
12184
+ {
12185
+ // increase and decrease
12186
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
12187
+ assert_eq!(pre_channel_value, 15_000);
12188
+ assert_eq!(post_channel_value, 17_000);
12189
+ }
12190
+ let base2: u64 = 2;
12191
+ let huge63i3 = (base2.pow(63) - 3) as i64;
12192
+ assert_eq!(huge63i3, 9223372036854775805);
12193
+ assert_eq!(-huge63i3, -9223372036854775805);
12194
+ {
12195
+ // increase, large amount
12196
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
12197
+ assert_eq!(pre_channel_value, 9_000);
12198
+ assert_eq!(post_channel_value, 9223372036854784807);
12199
+ }
12200
+ {
12201
+ // increase, large amounts
12202
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
12203
+ assert_eq!(pre_channel_value, 9_000);
12204
+ assert_eq!(post_channel_value, 9223372036854784807);
12205
+ }
12206
+ }
11918
12207
}
0 commit comments