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