@@ -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};
@@ -1427,6 +1426,30 @@ impl UnfundedChannelContext {
1427
1426
}
1428
1427
}
1429
1428
1429
+ /// Info about a pending splice, used in the pre-splice channel
1430
+ #[cfg(splicing)]
1431
+ #[derive(Clone)]
1432
+ struct PendingSpliceInfoPre {
1433
+ pub our_funding_contribution: i64,
1434
+ }
1435
+
1436
+ #[cfg(splicing)]
1437
+ impl PendingSpliceInfoPre {
1438
+ #[inline]
1439
+ fn add_checked(base: u64, delta: i64) -> u64 {
1440
+ if delta >= 0 {
1441
+ base.saturating_add(delta as u64)
1442
+ } else {
1443
+ base.saturating_sub(delta.abs() as u64)
1444
+ }
1445
+ }
1446
+
1447
+ /// Compute the post-splice channel value from the pre-splice values and the peer contributions
1448
+ pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1449
+ Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1450
+ }
1451
+ }
1452
+
1430
1453
/// Contains everything about the channel including state, and various flags.
1431
1454
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1432
1455
config: LegacyChannelConfig,
@@ -1462,6 +1485,10 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
1462
1485
secp_ctx: Secp256k1<secp256k1::All>,
1463
1486
channel_value_satoshis: u64,
1464
1487
1488
+ /// Info about an in-progress, pending splice (if any), on the pre-splice channel
1489
+ #[cfg(splicing)]
1490
+ pending_splice_pre: Option<PendingSpliceInfoPre>,
1491
+
1465
1492
latest_monitor_update_id: u64,
1466
1493
1467
1494
holder_signer: ChannelSignerType<SP>,
@@ -2516,6 +2543,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
2516
2543
is_manual_broadcast: false,
2517
2544
2518
2545
next_funding_txid: None,
2546
+
2547
+ #[cfg(splicing)]
2548
+ pending_splice_pre: None,
2519
2549
};
2520
2550
2521
2551
Ok(channel_context)
@@ -2746,6 +2776,9 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
2746
2776
local_initiated_shutdown: None,
2747
2777
is_manual_broadcast: false,
2748
2778
next_funding_txid: None,
2779
+
2780
+ #[cfg(splicing)]
2781
+ pending_splice_pre: None,
2749
2782
})
2750
2783
}
2751
2784
@@ -3928,6 +3961,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
3928
3961
(context.holder_selected_channel_reserve_satoshis, context.counterparty_selected_channel_reserve_satoshis)
3929
3962
}
3930
3963
3964
+ /// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
3965
+ /// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
3966
+ /// to checks with new channel value (before being comitted to it).
3967
+ #[cfg(splicing)]
3968
+ pub fn check_balance_meets_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
3969
+ if balance == 0 {
3970
+ return Ok(());
3971
+ }
3972
+ let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
3973
+ channel_value, self.holder_dust_limit_satoshis);
3974
+ if balance < holder_selected_channel_reserve_satoshis {
3975
+ return Err(ChannelError::Warn(format!(
3976
+ "Balance below reserve mandated by holder, {} vs {}",
3977
+ balance, holder_selected_channel_reserve_satoshis,
3978
+ )));
3979
+ }
3980
+ let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
3981
+ channel_value, self.counterparty_dust_limit_satoshis);
3982
+ if balance < counterparty_selected_channel_reserve_satoshis {
3983
+ return Err(ChannelError::Warn(format!(
3984
+ "Balance below reserve mandated by counterparty, {} vs {}",
3985
+ balance, counterparty_selected_channel_reserve_satoshis,
3986
+ )));
3987
+ }
3988
+ Ok(())
3989
+ }
3990
+
3931
3991
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
3932
3992
/// number of pending HTLCs that are on track to be in our next commitment tx.
3933
3993
///
@@ -4390,6 +4450,38 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4390
4450
self.channel_transaction_parameters = channel_transaction_parameters;
4391
4451
self.get_initial_counterparty_commitment_signature(logger)
4392
4452
}
4453
+
4454
+ /// Get the splice message that can be sent during splice initiation.
4455
+ #[cfg(splicing)]
4456
+ pub fn get_splice_init(&self, our_funding_contribution_satoshis: i64,
4457
+ funding_feerate_perkw: u32, locktime: u32,
4458
+ ) -> msgs::SpliceInit {
4459
+ // Reuse the existing funding pubkey, in spite of the channel value changing
4460
+ // (though at this point we don't know the new value yet, due tue the optional counterparty contribution)
4461
+ // Note that channel_keys_id is supposed NOT to change
4462
+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey.clone();
4463
+ msgs::SpliceInit {
4464
+ channel_id: self.channel_id,
4465
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4466
+ funding_feerate_perkw,
4467
+ locktime,
4468
+ funding_pubkey,
4469
+ require_confirmed_inputs: None,
4470
+ }
4471
+ }
4472
+
4473
+ /// Get the splice_ack message that can be sent in response to splice initiation.
4474
+ #[cfg(splicing)]
4475
+ pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
4476
+ // Reuse the existing funding pubkey, in spite of the channel value changing
4477
+ let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
4478
+ msgs::SpliceAck {
4479
+ channel_id: self.channel_id,
4480
+ funding_contribution_satoshis: our_funding_contribution_satoshis,
4481
+ funding_pubkey,
4482
+ require_confirmed_inputs: None,
4483
+ }
4484
+ }
4393
4485
}
4394
4486
4395
4487
// Internal utility functions for channels
@@ -8132,6 +8224,124 @@ impl<SP: Deref> FundedChannel<SP> where
8132
8224
}
8133
8225
}
8134
8226
8227
+ /// Initiate splicing
8228
+ #[cfg(splicing)]
8229
+ pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
8230
+ funding_feerate_perkw: u32, locktime: u32,
8231
+ ) -> Result<msgs::SpliceInit, ChannelError> {
8232
+ // Check if a splice has been initiated already.
8233
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8234
+ if let Some(splice_info) = &self.context.pending_splice_pre {
8235
+ return Err(ChannelError::Warn(format!(
8236
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
8237
+ )));
8238
+ }
8239
+
8240
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8241
+ return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
8242
+ }
8243
+
8244
+ let pre_channel_value = self.context.get_value_satoshis();
8245
+ // Sanity check: capacity cannot decrease below 0
8246
+ if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
8247
+ return Err(ChannelError::Warn(format!(
8248
+ "Post-splicing channel value cannot be negative. It was {} + {}",
8249
+ pre_channel_value, our_funding_contribution_satoshis
8250
+ )));
8251
+ }
8252
+
8253
+ if our_funding_contribution_satoshis < 0 {
8254
+ return Err(ChannelError::Warn(format!(
8255
+ "TODO(splicing): Splice-out not supported, only splice in, contribution {}",
8256
+ our_funding_contribution_satoshis,
8257
+ )));
8258
+ }
8259
+
8260
+ // Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
8261
+ // (Cannot test for miminum required post-splice channel value)
8262
+
8263
+ self.context.pending_splice_pre = Some(PendingSpliceInfoPre {
8264
+ our_funding_contribution: our_funding_contribution_satoshis,
8265
+ });
8266
+
8267
+ let msg = self.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
8268
+ Ok(msg)
8269
+ }
8270
+
8271
+ /// Handle splice_init
8272
+ #[cfg(splicing)]
8273
+ pub fn splice_init(&mut self, msg: &msgs::SpliceInit) -> Result<msgs::SpliceAck, ChannelError> {
8274
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8275
+ // TODO(splicing): Currently not possible to contribute on the splicing-acceptor side
8276
+ let our_funding_contribution_satoshis = 0i64;
8277
+
8278
+ // Check if a splice has been initiated already.
8279
+ // Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
8280
+ if let Some(splice_info) = &self.context.pending_splice_pre {
8281
+ return Err(ChannelError::Warn(format!(
8282
+ "Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
8283
+ )));
8284
+ }
8285
+
8286
+ if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
8287
+ return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
8288
+ }
8289
+
8290
+ let pre_channel_value = self.context.get_value_satoshis();
8291
+ // Sanity check: capacity cannot decrease below 0
8292
+ if (pre_channel_value as i64)
8293
+ .saturating_add(their_funding_contribution_satoshis)
8294
+ .saturating_add(our_funding_contribution_satoshis) < 0
8295
+ {
8296
+ return Err(ChannelError::Warn(format!(
8297
+ "Post-splicing channel value cannot be negative. It was {} + {} + {}",
8298
+ pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8299
+ )));
8300
+ }
8301
+
8302
+ if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
8303
+ return Err(ChannelError::Warn(format!(
8304
+ "Splice-out not supported, only splice in, relative {} + {}",
8305
+ their_funding_contribution_satoshis, our_funding_contribution_satoshis,
8306
+ )));
8307
+ }
8308
+
8309
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
8310
+ let post_balance = PendingSpliceInfoPre::add_checked(self.context.value_to_self_msat, our_funding_contribution_satoshis);
8311
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8312
+ // This will also be checked later at tx_complete
8313
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8314
+
8315
+ // TODO(splicing): Store msg.funding_pubkey
8316
+ // TODO(splicing): Apply start of splice (splice_start)
8317
+
8318
+ let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis);
8319
+ // TODO(splicing): start interactive funding negotiation
8320
+ Ok(splice_ack_msg)
8321
+ }
8322
+
8323
+ /// Handle splice_ack
8324
+ #[cfg(splicing)]
8325
+ pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
8326
+ let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8327
+
8328
+ // check if splice is pending
8329
+ let pending_splice = if let Some(pending_splice) = &self.context.pending_splice_pre {
8330
+ pending_splice
8331
+ } else {
8332
+ return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8333
+ };
8334
+
8335
+ let our_funding_contribution = pending_splice.our_funding_contribution;
8336
+
8337
+ let pre_channel_value = self.context.get_value_satoshis();
8338
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8339
+ let post_balance = PendingSpliceInfoPre::add_checked(self.context.value_to_self_msat, our_funding_contribution);
8340
+ // Early check for reserve requirement, assuming maximum balance of full channel value
8341
+ // This will also be checked later at tx_complete
8342
+ let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8343
+ Ok(())
8344
+ }
8135
8345
8136
8346
// Send stuff to our remote peers:
8137
8347
@@ -10531,6 +10741,9 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
10531
10741
// during a signing session, but have not received `tx_signatures` we MUST set `next_funding_txid`
10532
10742
// to the txid of that interactive transaction, else we MUST NOT set it.
10533
10743
next_funding_txid: None,
10744
+
10745
+ #[cfg(splicing)]
10746
+ pending_splice_pre: None,
10534
10747
},
10535
10748
interactive_tx_signing_session: None,
10536
10749
holder_commitment_point,
@@ -12316,4 +12529,69 @@ mod tests {
12316
12529
assert_eq!(node_a_chan.context.channel_state, ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::THEIR_CHANNEL_READY));
12317
12530
assert!(node_a_chan.check_get_channel_ready(0, &&logger).is_some());
12318
12531
}
12532
+
12533
+ #[cfg(all(test, splicing))]
12534
+ fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
12535
+ use crate::ln::channel::PendingSpliceInfoPre;
12536
+
12537
+ let post_channel_value = PendingSpliceInfoPre::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
12538
+ (pre_channel_value, post_channel_value)
12539
+ }
12540
+
12541
+ #[cfg(all(test, splicing))]
12542
+ #[test]
12543
+ fn test_splice_compute_post_value() {
12544
+ {
12545
+ // increase, small amounts
12546
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
12547
+ assert_eq!(pre_channel_value, 9_000);
12548
+ assert_eq!(post_channel_value, 15_000);
12549
+ }
12550
+ {
12551
+ // increase, small amounts
12552
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
12553
+ assert_eq!(pre_channel_value, 9_000);
12554
+ assert_eq!(post_channel_value, 15_000);
12555
+ }
12556
+ {
12557
+ // increase, small amounts
12558
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
12559
+ assert_eq!(pre_channel_value, 9_000);
12560
+ assert_eq!(post_channel_value, 15_000);
12561
+ }
12562
+ {
12563
+ // decrease, small amounts
12564
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
12565
+ assert_eq!(pre_channel_value, 15_000);
12566
+ assert_eq!(post_channel_value, 9_000);
12567
+ }
12568
+ {
12569
+ // decrease, small amounts
12570
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
12571
+ assert_eq!(pre_channel_value, 15_000);
12572
+ assert_eq!(post_channel_value, 9_000);
12573
+ }
12574
+ {
12575
+ // increase and decrease
12576
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
12577
+ assert_eq!(pre_channel_value, 15_000);
12578
+ assert_eq!(post_channel_value, 17_000);
12579
+ }
12580
+ let base2: u64 = 2;
12581
+ let huge63i3 = (base2.pow(63) - 3) as i64;
12582
+ assert_eq!(huge63i3, 9223372036854775805);
12583
+ assert_eq!(-huge63i3, -9223372036854775805);
12584
+ {
12585
+ // increase, large amount
12586
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
12587
+ assert_eq!(pre_channel_value, 9_000);
12588
+ assert_eq!(post_channel_value, 9223372036854784807);
12589
+ }
12590
+ {
12591
+ // increase, large amounts
12592
+ let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
12593
+ assert_eq!(pre_channel_value, 9_000);
12594
+ assert_eq!(post_channel_value, 9223372036854784807);
12595
+ }
12596
+ }
12319
12597
}
0 commit comments