@@ -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};
@@ -1649,6 +1648,30 @@ impl FundingScope {
1649
1648
}
1650
1649
}
1651
1650
1651
+ /// Info about a pending splice, used in the pre-splice channel
1652
+ #[cfg(splicing)]
1653
+ #[derive(Clone)]
1654
+ struct PendingSplice {
1655
+ pub our_funding_contribution: i64,
1656
+ }
1657
+
1658
+ #[cfg(splicing)]
1659
+ impl PendingSplice {
1660
+ #[inline]
1661
+ fn add_checked(base: u64, delta: i64) -> u64 {
1662
+ if delta >= 0 {
1663
+ base.saturating_add(delta as u64)
1664
+ } else {
1665
+ base.saturating_sub(delta.abs() as u64)
1666
+ }
1667
+ }
1668
+
1669
+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1670
+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1671
+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1672
+ }
1673
+ }
1674
+
1652
1675
/// Contains everything about the channel including state, and various flags.
1653
1676
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1654
1677
config: LegacyChannelConfig,
@@ -2290,6 +2313,8 @@ impl<SP: Deref> PendingV2Channel<SP> where SP::Target: SignerProvider {
2290
2313
context: self.context,
2291
2314
interactive_tx_signing_session: Some(signing_session),
2292
2315
holder_commitment_point,
2316
+ #[cfg(splicing)]
2317
+ pending_splice_pre: None,
2293
2318
};
2294
2319
Ok((funded_chan, commitment_signed, funding_ready_for_sig_event))
2295
2320
},
@@ -4068,6 +4093,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4068
4093
}
4069
4094
}
4070
4095
4096
+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
4097
+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
4098
+ /// to checks with new channel value (before being comitted to it).
4099
+ #[cfg(splicing)]
4100
+ pub fn check_balance_meets_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
4101
+ if balance == 0 {
4102
+ return Ok(());
4103
+ }
4104
+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4105
+ channel_value, self.holder_dust_limit_satoshis);
4106
+ if balance < holder_selected_channel_reserve_satoshis {
4107
+ return Err(ChannelError::Warn(format!(
4108
+ "Balance below reserve mandated by holder, {} vs {}",
4109
+ balance, holder_selected_channel_reserve_satoshis,
4110
+ )));
4111
+ }
4112
+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
4113
+ channel_value, self.counterparty_dust_limit_satoshis);
4114
+ if balance < counterparty_selected_channel_reserve_satoshis {
4115
+ return Err(ChannelError::Warn(format!(
4116
+ "Balance below reserve mandated by counterparty, {} vs {}",
4117
+ balance, counterparty_selected_channel_reserve_satoshis,
4118
+ )));
4119
+ }
4120
+ Ok(())
4121
+ }
4122
+
4071
4123
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
4072
4124
/// number of pending HTLCs that are on track to be in our next commitment tx.
4073
4125
///
@@ -4538,6 +4590,38 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4538
4590
self.channel_transaction_parameters = channel_transaction_parameters;
4539
4591
self.get_initial_counterparty_commitment_signature(funding, logger)
4540
4592
}
4593
+
4594
+ /// Get the splice message that can be sent during splice initiation.
4595
+ #[cfg(splicing)]
4596
+ pub fn get_splice_init(&self, our_funding_contribution_satoshis: i64,
4597
+ funding_feerate_perkw: u32, locktime: u32,
4598
+ ) -> msgs::SpliceInit {
4599
+ // Reuse the existing funding pubkey, in spite of the channel value changing
4600
+ // (though at this point we don't know the new value yet, due tue the optional counterparty contribution)
4601
+ // Note that channel_keys_id is supposed NOT to change
4602
+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey.clone();
4603
+ msgs::SpliceInit {
4604
+ channel_id: self.channel_id,
4605
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4606
+ funding_feerate_perkw,
4607
+ locktime,
4608
+ funding_pubkey,
4609
+ require_confirmed_inputs: None,
4610
+ }
4611
+ }
4612
+
4613
+ /// Get the splice_ack message that can be sent in response to splice initiation.
4614
+ #[cfg(splicing)]
4615
+ pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
4616
+ // Reuse the existing funding pubkey, in spite of the channel value changing
4617
+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
4618
+ msgs::SpliceAck {
4619
+ channel_id: self.channel_id,
4620
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4621
+ funding_pubkey,
4622
+ require_confirmed_inputs: None,
4623
+ }
4624
+ }
4541
4625
}
4542
4626
4543
4627
// Internal utility functions for channels
@@ -4650,6 +4734,9 @@ pub(super) struct FundedChannel<SP: Deref> where SP::Target: SignerProvider {
4650
4734
pub context: ChannelContext<SP>,
4651
4735
pub interactive_tx_signing_session: Option<InteractiveTxSigningSession>,
4652
4736
holder_commitment_point: HolderCommitmentPoint,
4737
+ /// Info about an in-progress, pending splice (if any), on the pre-splice channel
4738
+ #[cfg(splicing)]
4739
+ pending_splice_pre: Option<PendingSplice>,
4653
4740
}
4654
4741
4655
4742
#[cfg(any(test, fuzzing))]
@@ -8277,6 +8364,135 @@ impl<SP: Deref> FundedChannel<SP> where
8277
8364
}
8278
8365
}
8279
8366
8367
+ /// Initiate splicing
8368
+ #[cfg(splicing)]
8369
+ pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
8370
+ our_funding_inputs: Vec<(TxIn, Transaction)>, funding_feerate_perkw: u32, locktime: u32,
8371
+ ) -> Result<msgs::SpliceInit, ChannelError> {
8372
+ // Check if a splice has been initiated already.
8373
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8374
+ if let Some(splice_info) = &self.pending_splice_pre {
8375
+ return Err(ChannelError::Warn(format!(
8376
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
8377
+ )));
8378
+ }
8379
+
8380
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8381
+ return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
8382
+ }
8383
+
8384
+ let pre_channel_value = self.funding.get_value_satoshis();
8385
+ // Sanity check: capacity cannot decrease below 0
8386
+ if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
8387
+ return Err(ChannelError::Warn(format!(
8388
+ "Post-splicing channel value cannot be negative. It was {} + {}",
8389
+ pre_channel_value, our_funding_contribution_satoshis
8390
+ )));
8391
+ }
8392
+
8393
+ if our_funding_contribution_satoshis < 0 {
8394
+ return Err(ChannelError::Warn(format!(
8395
+ "TODO(splicing): Splice-out not supported, only splice in, contribution {}",
8396
+ our_funding_contribution_satoshis,
8397
+ )));
8398
+ }
8399
+
8400
+ // Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
8401
+ // (Cannot test for miminum required post-splice channel value)
8402
+
8403
+ // Check that inputs are sufficient to cover our contribution
8404
+ let sum_input: i64 = our_funding_inputs.into_iter().map(
8405
+ |(txin, tx)| tx.output.get(txin.previous_output.vout as usize).map(|tx| tx.value.to_sat() as i64).unwrap_or(0)
8406
+ ).sum();
8407
+ if sum_input < our_funding_contribution_satoshis {
8408
+ return Err(ChannelError::Warn(format!(
8409
+ "Provided inputs are insufficient for our contribution, {} {}",
8410
+ sum_input, our_funding_contribution_satoshis,
8411
+ )));
8412
+ }
8413
+
8414
+ self.pending_splice_pre = Some(PendingSplice {
8415
+ our_funding_contribution: our_funding_contribution_satoshis,
8416
+ });
8417
+
8418
+ let msg = self.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
8419
+ Ok(msg)
8420
+ }
8421
+
8422
+ /// Handle splice_init
8423
+ #[cfg(splicing)]
8424
+ pub fn splice_init(&mut self, msg: &msgs::SpliceInit) -> Result<msgs::SpliceAck, ChannelError> {
8425
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8426
+ // TODO(splicing): Currently not possible to contribute on the splicing-acceptor side
8427
+ let our_funding_contribution_satoshis = 0i64;
8428
+
8429
+ // Check if a splice has been initiated already.
8430
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8431
+ if let Some(splice_info) = &self.pending_splice_pre {
8432
+ return Err(ChannelError::Warn(format!(
8433
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
8434
+ )));
8435
+ }
8436
+
8437
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8438
+ return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
8439
+ }
8440
+
8441
+ let pre_channel_value = self.funding.get_value_satoshis();
8442
+ // Sanity check: capacity cannot decrease below 0
8443
+ if (pre_channel_value as i64)
8444
+ .saturating_add(their_funding_contribution_satoshis)
8445
+ .saturating_add(our_funding_contribution_satoshis) < 0
8446
+ {
8447
+ return Err(ChannelError::Warn(format!(
8448
+ "Post-splicing channel value cannot be negative. It was {} + {} + {}",
8449
+ pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8450
+ )));
8451
+ }
8452
+
8453
+ if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
8454
+ return Err(ChannelError::Warn(format!(
8455
+ "Splice-out not supported, only splice in, relative {} + {}",
8456
+ their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8457
+ )));
8458
+ }
8459
+
8460
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
8461
+ let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution_satoshis);
8462
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8463
+ // This will also be checked later at tx_complete
8464
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8465
+
8466
+ // TODO(splicing): Store msg.funding_pubkey
8467
+ // TODO(splicing): Apply start of splice (splice_start)
8468
+
8469
+ let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis);
8470
+ // TODO(splicing): start interactive funding negotiation
8471
+ Ok(splice_ack_msg)
8472
+ }
8473
+
8474
+ /// Handle splice_ack
8475
+ #[cfg(splicing)]
8476
+ pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
8477
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8478
+
8479
+ // check if splice is pending
8480
+ let pending_splice = if let Some(pending_splice) = &self.pending_splice_pre {
8481
+ pending_splice
8482
+ } else {
8483
+ return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8484
+ };
8485
+
8486
+ let our_funding_contribution = pending_splice.our_funding_contribution;
8487
+
8488
+ let pre_channel_value = self.funding.get_value_satoshis();
8489
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8490
+ let post_balance = PendingSplice::add_checked(self.funding.value_to_self_msat, our_funding_contribution);
8491
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8492
+ // This will also be checked later at tx_complete
8493
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8494
+ Ok(())
8495
+ }
8280
8496
8281
8497
// Send stuff to our remote peers:
8282
8498
@@ -9191,6 +9407,8 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
9191
9407
context: self.context,
9192
9408
interactive_tx_signing_session: None,
9193
9409
holder_commitment_point,
9410
+ #[cfg(splicing)]
9411
+ pending_splice_pre: None,
9194
9412
};
9195
9413
9196
9414
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
@@ -9458,6 +9676,8 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
9458
9676
context: self.context,
9459
9677
interactive_tx_signing_session: None,
9460
9678
holder_commitment_point,
9679
+ #[cfg(splicing)]
9680
+ pending_splice_pre: None,
9461
9681
};
9462
9682
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
9463
9683
|| channel.context.signer_pending_channel_ready;
@@ -10849,6 +11069,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
10849
11069
},
10850
11070
interactive_tx_signing_session: None,
10851
11071
holder_commitment_point,
11072
+ #[cfg(splicing)]
11073
+ pending_splice_pre: None,
10852
11074
})
10853
11075
}
10854
11076
}
@@ -12667,4 +12889,69 @@ mod tests {
12667
12889
320
12668
12890
);
12669
12891
}
12892
+
12893
+ #[cfg(all(test, splicing))]
12894
+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
12895
+ use crate::ln::channel::PendingSplice;
12896
+
12897
+ let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
12898
+ (pre_channel_value, post_channel_value)
12899
+ }
12900
+
12901
+ #[cfg(all(test, splicing))]
12902
+ #[test]
12903
+ fn test_splice_compute_post_value() {
12904
+ {
12905
+ // increase, small amounts
12906
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
12907
+ assert_eq!(pre_channel_value, 9_000);
12908
+ assert_eq!(post_channel_value, 15_000);
12909
+ }
12910
+ {
12911
+ // increase, small amounts
12912
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
12913
+ assert_eq!(pre_channel_value, 9_000);
12914
+ assert_eq!(post_channel_value, 15_000);
12915
+ }
12916
+ {
12917
+ // increase, small amounts
12918
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
12919
+ assert_eq!(pre_channel_value, 9_000);
12920
+ assert_eq!(post_channel_value, 15_000);
12921
+ }
12922
+ {
12923
+ // decrease, small amounts
12924
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
12925
+ assert_eq!(pre_channel_value, 15_000);
12926
+ assert_eq!(post_channel_value, 9_000);
12927
+ }
12928
+ {
12929
+ // decrease, small amounts
12930
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
12931
+ assert_eq!(pre_channel_value, 15_000);
12932
+ assert_eq!(post_channel_value, 9_000);
12933
+ }
12934
+ {
12935
+ // increase and decrease
12936
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
12937
+ assert_eq!(pre_channel_value, 15_000);
12938
+ assert_eq!(post_channel_value, 17_000);
12939
+ }
12940
+ let base2: u64 = 2;
12941
+ let huge63i3 = (base2.pow(63) - 3) as i64;
12942
+ assert_eq!(huge63i3, 9223372036854775805);
12943
+ assert_eq!(-huge63i3, -9223372036854775805);
12944
+ {
12945
+ // increase, large amount
12946
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
12947
+ assert_eq!(pre_channel_value, 9_000);
12948
+ assert_eq!(post_channel_value, 9223372036854784807);
12949
+ }
12950
+ {
12951
+ // increase, large amounts
12952
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
12953
+ assert_eq!(pre_channel_value, 9_000);
12954
+ assert_eq!(post_channel_value, 9223372036854784807);
12955
+ }
12956
+ }
12670
12957
}
0 commit comments