@@ -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};
@@ -1541,6 +1540,30 @@ impl UnfundedChannelContext {
1541
1540
}
1542
1541
}
1543
1542
1543
+ /// Info about a pending splice, used in the pre-splice channel
1544
+ #[cfg(splicing)]
1545
+ #[derive(Clone)]
1546
+ struct PendingSpliceInfoPre {
1547
+ pub our_funding_contribution: i64,
1548
+ }
1549
+
1550
+ #[cfg(splicing)]
1551
+ impl PendingSpliceInfoPre {
1552
+ #[inline]
1553
+ fn add_checked(base: u64, delta: i64) -> u64 {
1554
+ if delta >= 0 {
1555
+ base.saturating_add(delta as u64)
1556
+ } else {
1557
+ base.saturating_sub(delta.abs() as u64)
1558
+ }
1559
+ }
1560
+
1561
+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1562
+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1563
+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1564
+ }
1565
+ }
1566
+
1544
1567
/// Contains everything about the channel including state, and various flags.
1545
1568
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1546
1569
config: LegacyChannelConfig,
@@ -1576,6 +1599,10 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1576
1599
secp_ctx: Secp256k1<secp256k1::All>,
1577
1600
channel_value_satoshis: u64,
1578
1601
1602
+ /// Info about an in-progress, pending splice (if any), on the pre-splice channel
1603
+ #[cfg(splicing)]
1604
+ pending_splice_pre: Option<PendingSpliceInfoPre>,
1605
+
1579
1606
latest_monitor_update_id: u64,
1580
1607
1581
1608
holder_signer: ChannelSignerType<SP>,
@@ -2638,6 +2665,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
2638
2665
is_manual_broadcast: false,
2639
2666
2640
2667
next_funding_txid: None,
2668
+
2669
+ #[cfg(splicing)]
2670
+ pending_splice_pre: None,
2641
2671
};
2642
2672
2643
2673
Ok(channel_context)
@@ -2865,6 +2895,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
2865
2895
local_initiated_shutdown: None,
2866
2896
is_manual_broadcast: false,
2867
2897
next_funding_txid: None,
2898
+
2899
+ #[cfg(splicing)]
2900
+ pending_splice_pre: None,
2868
2901
})
2869
2902
}
2870
2903
@@ -4047,6 +4080,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4047
4080
(context.holder_selected_channel_reserve_satoshis, context.counterparty_selected_channel_reserve_satoshis)
4048
4081
}
4049
4082
4083
+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
4084
+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
4085
+ /// to checks with new channel value (before being comitted to it).
4086
+ #[cfg(splicing)]
4087
+ pub fn check_balance_meets_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
4088
+ if balance == 0 {
4089
+ return Ok(());
4090
+ }
4091
+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4092
+ channel_value, self.holder_dust_limit_satoshis);
4093
+ if balance < holder_selected_channel_reserve_satoshis {
4094
+ return Err(ChannelError::Warn(format!(
4095
+ "Balance below reserve mandated by holder, {} vs {}",
4096
+ balance, holder_selected_channel_reserve_satoshis,
4097
+ )));
4098
+ }
4099
+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4100
+ channel_value, self.counterparty_dust_limit_satoshis);
4101
+ if balance < counterparty_selected_channel_reserve_satoshis {
4102
+ return Err(ChannelError::Warn(format!(
4103
+ "Balance below reserve mandated by counterparty, {} vs {}",
4104
+ balance, counterparty_selected_channel_reserve_satoshis,
4105
+ )));
4106
+ }
4107
+ Ok(())
4108
+ }
4109
+
4050
4110
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
4051
4111
/// number of pending HTLCs that are on track to be in our next commitment tx.
4052
4112
///
@@ -4513,6 +4573,38 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4513
4573
self.channel_transaction_parameters = channel_transaction_parameters;
4514
4574
self.get_initial_counterparty_commitment_signature(logger)
4515
4575
}
4576
+
4577
+ /// Get the splice message that can be sent during splice initiation.
4578
+ #[cfg(splicing)]
4579
+ pub fn get_splice_init(&self, our_funding_contribution_satoshis: i64,
4580
+ funding_feerate_perkw: u32, locktime: u32,
4581
+ ) -> msgs::SpliceInit {
4582
+ // Reuse the existing funding pubkey, in spite of the channel value changing
4583
+ // (though at this point we don't know the new value yet, due tue the optional counterparty contribution)
4584
+ // Note that channel_keys_id is supposed NOT to change
4585
+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey.clone();
4586
+ msgs::SpliceInit {
4587
+ channel_id: self.channel_id,
4588
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4589
+ funding_feerate_perkw,
4590
+ locktime,
4591
+ funding_pubkey,
4592
+ require_confirmed_inputs: None,
4593
+ }
4594
+ }
4595
+
4596
+ /// Get the splice_ack message that can be sent in response to splice initiation.
4597
+ #[cfg(splicing)]
4598
+ pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
4599
+ // Reuse the existing funding pubkey, in spite of the channel value changing
4600
+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
4601
+ msgs::SpliceAck {
4602
+ channel_id: self.channel_id,
4603
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4604
+ funding_pubkey,
4605
+ require_confirmed_inputs: None,
4606
+ }
4607
+ }
4516
4608
}
4517
4609
4518
4610
// Internal utility functions for channels
@@ -8220,6 +8312,124 @@ impl<SP: Deref> FundedChannel<SP> where
8220
8312
}
8221
8313
}
8222
8314
8315
+ /// Initiate splicing
8316
+ #[cfg(splicing)]
8317
+ pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
8318
+ funding_feerate_perkw: u32, locktime: u32,
8319
+ ) -> Result<msgs::SpliceInit, ChannelError> {
8320
+ // Check if a splice has been initiated already.
8321
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8322
+ if let Some(splice_info) = &self.context.pending_splice_pre {
8323
+ return Err(ChannelError::Warn(format!(
8324
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
8325
+ )));
8326
+ }
8327
+
8328
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8329
+ return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
8330
+ }
8331
+
8332
+ let pre_channel_value = self.context.get_value_satoshis();
8333
+ // Sanity check: capacity cannot decrease below 0
8334
+ if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
8335
+ return Err(ChannelError::Warn(format!(
8336
+ "Post-splicing channel value cannot be negative. It was {} + {}",
8337
+ pre_channel_value, our_funding_contribution_satoshis
8338
+ )));
8339
+ }
8340
+
8341
+ if our_funding_contribution_satoshis < 0 {
8342
+ return Err(ChannelError::Warn(format!(
8343
+ "TODO(splicing): Splice-out not supported, only splice in, contribution {}",
8344
+ our_funding_contribution_satoshis,
8345
+ )));
8346
+ }
8347
+
8348
+ // Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
8349
+ // (Cannot test for miminum required post-splice channel value)
8350
+
8351
+ self.context.pending_splice_pre = Some(PendingSpliceInfoPre {
8352
+ our_funding_contribution: our_funding_contribution_satoshis,
8353
+ });
8354
+
8355
+ let msg = self.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
8356
+ Ok(msg)
8357
+ }
8358
+
8359
+ /// Handle splice_init
8360
+ #[cfg(splicing)]
8361
+ pub fn splice_init(&mut self, msg: &msgs::SpliceInit) -> Result<msgs::SpliceAck, ChannelError> {
8362
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8363
+ // TODO(splicing): Currently not possible to contribute on the splicing-acceptor side
8364
+ let our_funding_contribution_satoshis = 0i64;
8365
+
8366
+ // Check if a splice has been initiated already.
8367
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8368
+ if let Some(splice_info) = &self.context.pending_splice_pre {
8369
+ return Err(ChannelError::Warn(format!(
8370
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
8371
+ )));
8372
+ }
8373
+
8374
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8375
+ return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
8376
+ }
8377
+
8378
+ let pre_channel_value = self.context.get_value_satoshis();
8379
+ // Sanity check: capacity cannot decrease below 0
8380
+ if (pre_channel_value as i64)
8381
+ .saturating_add(their_funding_contribution_satoshis)
8382
+ .saturating_add(our_funding_contribution_satoshis) < 0
8383
+ {
8384
+ return Err(ChannelError::Warn(format!(
8385
+ "Post-splicing channel value cannot be negative. It was {} + {} + {}",
8386
+ pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8387
+ )));
8388
+ }
8389
+
8390
+ if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
8391
+ return Err(ChannelError::Warn(format!(
8392
+ "Splice-out not supported, only splice in, relative {} + {}",
8393
+ their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8394
+ )));
8395
+ }
8396
+
8397
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
8398
+ let post_balance = PendingSpliceInfoPre::add_checked(self.context.value_to_self_msat, our_funding_contribution_satoshis);
8399
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8400
+ // This will also be checked later at tx_complete
8401
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8402
+
8403
+ // TODO(splicing): Store msg.funding_pubkey
8404
+ // TODO(splicing): Apply start of splice (splice_start)
8405
+
8406
+ let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis);
8407
+ // TODO(splicing): start interactive funding negotiation
8408
+ Ok(splice_ack_msg)
8409
+ }
8410
+
8411
+ /// Handle splice_ack
8412
+ #[cfg(splicing)]
8413
+ pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
8414
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8415
+
8416
+ // check if splice is pending
8417
+ let pending_splice = if let Some(pending_splice) = &self.context.pending_splice_pre {
8418
+ pending_splice
8419
+ } else {
8420
+ return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8421
+ };
8422
+
8423
+ let our_funding_contribution = pending_splice.our_funding_contribution;
8424
+
8425
+ let pre_channel_value = self.context.get_value_satoshis();
8426
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8427
+ let post_balance = PendingSpliceInfoPre::add_checked(self.context.value_to_self_msat, our_funding_contribution);
8428
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8429
+ // This will also be checked later at tx_complete
8430
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8431
+ Ok(())
8432
+ }
8223
8433
8224
8434
// Send stuff to our remote peers:
8225
8435
@@ -10559,6 +10769,9 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
10559
10769
// during a signing session, but have not received `tx_signatures` we MUST set `next_funding_txid`
10560
10770
// to the txid of that interactive transaction, else we MUST NOT set it.
10561
10771
next_funding_txid: None,
10772
+
10773
+ #[cfg(splicing)]
10774
+ pending_splice_pre: None,
10562
10775
},
10563
10776
interactive_tx_signing_session: None,
10564
10777
holder_commitment_point,
@@ -12344,4 +12557,69 @@ mod tests {
12344
12557
assert_eq!(node_a_chan.context.channel_state, ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::THEIR_CHANNEL_READY));
12345
12558
assert!(node_a_chan.check_get_channel_ready(0, &&logger).is_some());
12346
12559
}
12560
+
12561
+ #[cfg(all(test, splicing))]
12562
+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
12563
+ use crate::ln::channel::PendingSpliceInfoPre;
12564
+
12565
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
12566
+ (pre_channel_value, post_channel_value)
12567
+ }
12568
+
12569
+ #[cfg(all(test, splicing))]
12570
+ #[test]
12571
+ fn test_splice_compute_post_value() {
12572
+ {
12573
+ // increase, small amounts
12574
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
12575
+ assert_eq!(pre_channel_value, 9_000);
12576
+ assert_eq!(post_channel_value, 15_000);
12577
+ }
12578
+ {
12579
+ // increase, small amounts
12580
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
12581
+ assert_eq!(pre_channel_value, 9_000);
12582
+ assert_eq!(post_channel_value, 15_000);
12583
+ }
12584
+ {
12585
+ // increase, small amounts
12586
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
12587
+ assert_eq!(pre_channel_value, 9_000);
12588
+ assert_eq!(post_channel_value, 15_000);
12589
+ }
12590
+ {
12591
+ // decrease, small amounts
12592
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
12593
+ assert_eq!(pre_channel_value, 15_000);
12594
+ assert_eq!(post_channel_value, 9_000);
12595
+ }
12596
+ {
12597
+ // decrease, small amounts
12598
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
12599
+ assert_eq!(pre_channel_value, 15_000);
12600
+ assert_eq!(post_channel_value, 9_000);
12601
+ }
12602
+ {
12603
+ // increase and decrease
12604
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
12605
+ assert_eq!(pre_channel_value, 15_000);
12606
+ assert_eq!(post_channel_value, 17_000);
12607
+ }
12608
+ let base2: u64 = 2;
12609
+ let huge63i3 = (base2.pow(63) - 3) as i64;
12610
+ assert_eq!(huge63i3, 9223372036854775805);
12611
+ assert_eq!(-huge63i3, -9223372036854775805);
12612
+ {
12613
+ // increase, large amount
12614
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
12615
+ assert_eq!(pre_channel_value, 9_000);
12616
+ assert_eq!(post_channel_value, 9223372036854784807);
12617
+ }
12618
+ {
12619
+ // increase, large amounts
12620
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
12621
+ assert_eq!(pre_channel_value, 9_000);
12622
+ assert_eq!(post_channel_value, 9223372036854784807);
12623
+ }
12624
+ }
12347
12625
}
0 commit comments