@@ -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};
@@ -1517,6 +1516,8 @@ impl<SP: Deref> Channel<SP> where
1517
1516
interactive_tx_signing_session: chan.interactive_tx_signing_session,
1518
1517
holder_commitment_point,
1519
1518
is_v2_established: true,
1519
+ #[cfg(splicing)]
1520
+ pending_splice_pre: None,
1520
1521
};
1521
1522
let res = funded_channel.commitment_signed_initial_v2(msg, best_block, signer_provider, logger)
1522
1523
.map(|monitor| (Some(monitor), None))
@@ -1719,6 +1720,30 @@ impl FundingScope {
1719
1720
}
1720
1721
}
1721
1722
1723
+ /// Info about a pending splice, used in the pre-splice channel
1724
+ #[cfg(splicing)]
1725
+ #[derive(Clone)]
1726
+ struct PendingSplice {
1727
+ pub our_funding_contribution: i64,
1728
+ }
1729
+
1730
+ #[cfg(splicing)]
1731
+ impl PendingSplice {
1732
+ #[inline]
1733
+ fn add_checked(base: u64, delta: i64) -> u64 {
1734
+ if delta >= 0 {
1735
+ base.saturating_add(delta as u64)
1736
+ } else {
1737
+ base.saturating_sub(delta.abs() as u64)
1738
+ }
1739
+ }
1740
+
1741
+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1742
+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1743
+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1744
+ }
1745
+ }
1746
+
1722
1747
/// Contains everything about the channel including state, and various flags.
1723
1748
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1724
1749
config: LegacyChannelConfig,
@@ -4105,6 +4130,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4105
4130
}
4106
4131
}
4107
4132
4133
+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
4134
+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
4135
+ /// to checks with new channel value (before being comitted to it).
4136
+ #[cfg(splicing)]
4137
+ pub fn check_balance_meets_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
4138
+ if balance == 0 {
4139
+ return Ok(());
4140
+ }
4141
+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4142
+ channel_value, self.holder_dust_limit_satoshis);
4143
+ if balance < holder_selected_channel_reserve_satoshis {
4144
+ return Err(ChannelError::Warn(format!(
4145
+ "Balance below reserve mandated by holder, {} vs {}",
4146
+ balance, holder_selected_channel_reserve_satoshis,
4147
+ )));
4148
+ }
4149
+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4150
+ channel_value, self.counterparty_dust_limit_satoshis);
4151
+ if balance < counterparty_selected_channel_reserve_satoshis {
4152
+ return Err(ChannelError::Warn(format!(
4153
+ "Balance below reserve mandated by counterparty, {} vs {}",
4154
+ balance, counterparty_selected_channel_reserve_satoshis,
4155
+ )));
4156
+ }
4157
+ Ok(())
4158
+ }
4159
+
4108
4160
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
4109
4161
/// number of pending HTLCs that are on track to be in our next commitment tx.
4110
4162
///
@@ -4696,6 +4748,9 @@ pub(super) struct FundedChannel<SP: Deref> where SP::Target: SignerProvider {
4696
4748
/// Indicates whether this funded channel had been established with V2 channel
4697
4749
/// establishment.
4698
4750
is_v2_established: bool,
4751
+ /// Info about an in-progress, pending splice (if any), on the pre-splice channel
4752
+ #[cfg(splicing)]
4753
+ pending_splice_pre: Option<PendingSplice>,
4699
4754
}
4700
4755
4701
4756
#[cfg(any(test, fuzzing))]
@@ -8323,6 +8378,167 @@ impl<SP: Deref> FundedChannel<SP> where
8323
8378
}
8324
8379
}
8325
8380
8381
+ /// Initiate splicing
8382
+ #[cfg(splicing)]
8383
+ pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
8384
+ our_funding_inputs: Vec<(TxIn, Transaction)>, funding_feerate_perkw: u32, locktime: u32,
8385
+ ) -> Result<msgs::SpliceInit, ChannelError> {
8386
+ // Check if a splice has been initiated already.
8387
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8388
+ if let Some(splice_info) = &self.pending_splice_pre {
8389
+ return Err(ChannelError::Warn(format!(
8390
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
8391
+ )));
8392
+ }
8393
+
8394
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8395
+ return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
8396
+ }
8397
+
8398
+ let pre_channel_value = self.funding.get_value_satoshis();
8399
+ // Sanity check: capacity cannot decrease below 0
8400
+ if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
8401
+ return Err(ChannelError::Warn(format!(
8402
+ "Post-splicing channel value cannot be negative. It was {} + {}",
8403
+ pre_channel_value, our_funding_contribution_satoshis
8404
+ )));
8405
+ }
8406
+
8407
+ if our_funding_contribution_satoshis < 0 {
8408
+ return Err(ChannelError::Warn(format!(
8409
+ "TODO(splicing): Splice-out not supported, only splice in, contribution {}",
8410
+ our_funding_contribution_satoshis,
8411
+ )));
8412
+ }
8413
+
8414
+ // Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
8415
+ // (Cannot test for miminum required post-splice channel value)
8416
+
8417
+ // Check that inputs are sufficient to cover our contribution
8418
+ let sum_input: i64 = our_funding_inputs.into_iter().map(
8419
+ |(txin, tx)| tx.output.get(txin.previous_output.vout as usize).map(|tx| tx.value.to_sat() as i64).unwrap_or(0)
8420
+ ).sum();
8421
+ if sum_input < our_funding_contribution_satoshis {
8422
+ return Err(ChannelError::Warn(format!(
8423
+ "Provided inputs are insufficient for our contribution, {} {}",
8424
+ sum_input, our_funding_contribution_satoshis,
8425
+ )));
8426
+ }
8427
+
8428
+ self.pending_splice_pre = Some(PendingSplice {
8429
+ our_funding_contribution: our_funding_contribution_satoshis,
8430
+ });
8431
+
8432
+ let msg = self.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
8433
+ Ok(msg)
8434
+ }
8435
+
8436
+ /// Get the splice message that can be sent during splice initiation.
8437
+ #[cfg(splicing)]
8438
+ pub fn get_splice_init(&self, our_funding_contribution_satoshis: i64,
8439
+ funding_feerate_perkw: u32, locktime: u32,
8440
+ ) -> msgs::SpliceInit {
8441
+ // Reuse the existing funding pubkey, in spite of the channel value changing
8442
+ // (though at this point we don't know the new value yet, due tue the optional counterparty contribution)
8443
+ // Note that channel_keys_id is supposed NOT to change
8444
+ let funding_pubkey = self.funding.get_holder_pubkeys().funding_pubkey.clone();
8445
+ msgs::SpliceInit {
8446
+ channel_id: self.context.channel_id,
8447
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
8448
+ funding_feerate_perkw,
8449
+ locktime,
8450
+ funding_pubkey,
8451
+ require_confirmed_inputs: None,
8452
+ }
8453
+ }
8454
+
8455
+ /// Handle splice_init
8456
+ #[cfg(splicing)]
8457
+ pub fn splice_init(&mut self, msg: &msgs::SpliceInit) -> Result<msgs::SpliceAck, ChannelError> {
8458
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8459
+ // TODO(splicing): Currently not possible to contribute on the splicing-acceptor side
8460
+ let our_funding_contribution_satoshis = 0i64;
8461
+
8462
+ // Check if a splice has been initiated already.
8463
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8464
+ if let Some(splice_info) = &self.pending_splice_pre {
8465
+ return Err(ChannelError::Warn(format!(
8466
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
8467
+ )));
8468
+ }
8469
+
8470
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8471
+ return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
8472
+ }
8473
+
8474
+ let pre_channel_value = self.funding.get_value_satoshis();
8475
+ // Sanity check: capacity cannot decrease below 0
8476
+ if (pre_channel_value as i64)
8477
+ .saturating_add(their_funding_contribution_satoshis)
8478
+ .saturating_add(our_funding_contribution_satoshis) < 0
8479
+ {
8480
+ return Err(ChannelError::Warn(format!(
8481
+ "Post-splicing channel value cannot be negative. It was {} + {} + {}",
8482
+ pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8483
+ )));
8484
+ }
8485
+
8486
+ if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
8487
+ return Err(ChannelError::Warn(format!(
8488
+ "Splice-out not supported, only splice in, relative {} + {}",
8489
+ their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8490
+ )));
8491
+ }
8492
+
8493
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
8494
+ let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution_satoshis);
8495
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8496
+ // This will also be checked later at tx_complete
8497
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8498
+
8499
+ // TODO(splicing): Store msg.funding_pubkey
8500
+ // TODO(splicing): Apply start of splice (splice_start)
8501
+
8502
+ let splice_ack_msg = self.get_splice_ack(our_funding_contribution_satoshis);
8503
+ // TODO(splicing): start interactive funding negotiation
8504
+ Ok(splice_ack_msg)
8505
+ }
8506
+
8507
+ /// Get the splice_ack message that can be sent in response to splice initiation.
8508
+ #[cfg(splicing)]
8509
+ pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
8510
+ // Reuse the existing funding pubkey, in spite of the channel value changing
8511
+ let funding_pubkey = self.funding.get_holder_pubkeys().funding_pubkey;
8512
+ msgs::SpliceAck {
8513
+ channel_id: self.context.channel_id,
8514
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
8515
+ funding_pubkey,
8516
+ require_confirmed_inputs: None,
8517
+ }
8518
+ }
8519
+
8520
+ /// Handle splice_ack
8521
+ #[cfg(splicing)]
8522
+ pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
8523
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8524
+
8525
+ // check if splice is pending
8526
+ let pending_splice = if let Some(pending_splice) = &self.pending_splice_pre {
8527
+ pending_splice
8528
+ } else {
8529
+ return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8530
+ };
8531
+
8532
+ let our_funding_contribution = pending_splice.our_funding_contribution;
8533
+
8534
+ let pre_channel_value = self.funding.get_value_satoshis();
8535
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8536
+ let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution);
8537
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8538
+ // This will also be checked later at tx_complete
8539
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8540
+ Ok(())
8541
+ }
8326
8542
8327
8543
// Send stuff to our remote peers:
8328
8544
@@ -9243,6 +9459,8 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
9243
9459
interactive_tx_signing_session: None,
9244
9460
is_v2_established: false,
9245
9461
holder_commitment_point,
9462
+ #[cfg(splicing)]
9463
+ pending_splice_pre: None,
9246
9464
};
9247
9465
9248
9466
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
@@ -9510,6 +9728,8 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
9510
9728
interactive_tx_signing_session: None,
9511
9729
is_v2_established: false,
9512
9730
holder_commitment_point,
9731
+ #[cfg(splicing)]
9732
+ pending_splice_pre: None,
9513
9733
};
9514
9734
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
9515
9735
|| channel.context.signer_pending_channel_ready;
@@ -10870,6 +11090,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, &'c Channel
10870
11090
interactive_tx_signing_session: None,
10871
11091
is_v2_established,
10872
11092
holder_commitment_point,
11093
+ #[cfg(splicing)]
11094
+ pending_splice_pre: None,
10873
11095
})
10874
11096
}
10875
11097
}
@@ -12684,4 +12906,69 @@ mod tests {
12684
12906
320
12685
12907
);
12686
12908
}
12909
+
12910
+ #[cfg(all(test, splicing))]
12911
+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
12912
+ use crate::ln::channel::PendingSplice;
12913
+
12914
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
12915
+ (pre_channel_value, post_channel_value)
12916
+ }
12917
+
12918
+ #[cfg(all(test, splicing))]
12919
+ #[test]
12920
+ fn test_splice_compute_post_value() {
12921
+ {
12922
+ // increase, small amounts
12923
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
12924
+ assert_eq!(pre_channel_value, 9_000);
12925
+ assert_eq!(post_channel_value, 15_000);
12926
+ }
12927
+ {
12928
+ // increase, small amounts
12929
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
12930
+ assert_eq!(pre_channel_value, 9_000);
12931
+ assert_eq!(post_channel_value, 15_000);
12932
+ }
12933
+ {
12934
+ // increase, small amounts
12935
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
12936
+ assert_eq!(pre_channel_value, 9_000);
12937
+ assert_eq!(post_channel_value, 15_000);
12938
+ }
12939
+ {
12940
+ // decrease, small amounts
12941
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
12942
+ assert_eq!(pre_channel_value, 15_000);
12943
+ assert_eq!(post_channel_value, 9_000);
12944
+ }
12945
+ {
12946
+ // decrease, small amounts
12947
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
12948
+ assert_eq!(pre_channel_value, 15_000);
12949
+ assert_eq!(post_channel_value, 9_000);
12950
+ }
12951
+ {
12952
+ // increase and decrease
12953
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
12954
+ assert_eq!(pre_channel_value, 15_000);
12955
+ assert_eq!(post_channel_value, 17_000);
12956
+ }
12957
+ let base2: u64 = 2;
12958
+ let huge63i3 = (base2.pow(63) - 3) as i64;
12959
+ assert_eq!(huge63i3, 9223372036854775805);
12960
+ assert_eq!(-huge63i3, -9223372036854775805);
12961
+ {
12962
+ // increase, large amount
12963
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
12964
+ assert_eq!(pre_channel_value, 9_000);
12965
+ assert_eq!(post_channel_value, 9223372036854784807);
12966
+ }
12967
+ {
12968
+ // increase, large amounts
12969
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
12970
+ assert_eq!(pre_channel_value, 9_000);
12971
+ assert_eq!(post_channel_value, 9223372036854784807);
12972
+ }
12973
+ }
12687
12974
}
0 commit comments