Skip to content

Commit 0a997dd

Browse files
committed
New splice_channel() for initiating splicing, handle splice_init and splice_ack messages, but fail afterwards
1 parent 0216d7d commit 0a997dd

File tree

5 files changed

+792
-11
lines changed

5 files changed

+792
-11
lines changed

lightning/src/ln/channel.rs

Lines changed: 289 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use bitcoin::amount::Amount;
1111
use bitcoin::constants::ChainHash;
1212
use bitcoin::script::{Script, ScriptBuf, Builder, WScriptHash};
1313
use bitcoin::transaction::{Transaction, TxIn};
14-
use bitcoin::sighash;
1514
use bitcoin::sighash::EcdsaSighashType;
1615
use bitcoin::consensus::encode;
1716
use bitcoin::absolute::LockTime;
@@ -25,7 +24,7 @@ use bitcoin::hash_types::{Txid, BlockHash};
2524
use bitcoin::secp256k1::constants::PUBLIC_KEY_SIZE;
2625
use bitcoin::secp256k1::{PublicKey,SecretKey};
2726
use bitcoin::secp256k1::{Secp256k1,ecdsa::Signature};
28-
use bitcoin::secp256k1;
27+
use bitcoin::{secp256k1, sighash};
2928

3029
use crate::ln::types::ChannelId;
3130
use crate::types::payment::{PaymentPreimage, PaymentHash};
@@ -1516,6 +1515,8 @@ impl<SP: Deref> Channel<SP> where
15161515
interactive_tx_signing_session: chan.interactive_tx_signing_session,
15171516
holder_commitment_point,
15181517
is_v2_established: true,
1518+
#[cfg(splicing)]
1519+
pending_splice_pre: None,
15191520
};
15201521
let res = funded_channel.commitment_signed_initial_v2(msg, best_block, signer_provider, logger)
15211522
.map(|monitor| (Some(monitor), None))
@@ -1718,6 +1719,30 @@ impl FundingScope {
17181719
}
17191720
}
17201721

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+
17211746
/// Contains everything about the channel including state, and various flags.
17221747
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
17231748
config: LegacyChannelConfig,
@@ -4104,6 +4129,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
41044129
}
41054130
}
41064131

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+
41074159
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
41084160
/// number of pending HTLCs that are on track to be in our next commitment tx.
41094161
///
@@ -4695,6 +4747,9 @@ pub(super) struct FundedChannel<SP: Deref> where SP::Target: SignerProvider {
46954747
/// Indicates whether this funded channel had been established with V2 channel
46964748
/// establishment.
46974749
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>,
46984753
}
46994754

47004755
#[cfg(any(test, fuzzing))]
@@ -8322,6 +8377,167 @@ impl<SP: Deref> FundedChannel<SP> where
83228377
}
83238378
}
83248379

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+
}
83258541

83268542
// Send stuff to our remote peers:
83278543

@@ -9242,6 +9458,8 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
92429458
interactive_tx_signing_session: None,
92439459
is_v2_established: false,
92449460
holder_commitment_point,
9461+
#[cfg(splicing)]
9462+
pending_splice_pre: None,
92459463
};
92469464

92479465
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 {
95099727
interactive_tx_signing_session: None,
95109728
is_v2_established: false,
95119729
holder_commitment_point,
9730+
#[cfg(splicing)]
9731+
pending_splice_pre: None,
95129732
};
95139733
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
95149734
|| 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
1086911089
interactive_tx_signing_session: None,
1087011090
is_v2_established,
1087111091
holder_commitment_point,
11092+
#[cfg(splicing)]
11093+
pending_splice_pre: None,
1087211094
})
1087311095
}
1087411096
}
@@ -12683,4 +12905,69 @@ mod tests {
1268312905
320
1268412906
);
1268512907
}
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+
}
1268612973
}

0 commit comments

Comments
 (0)