Skip to content

Commit ea76587

Browse files
committed
New splice_channel() for initiating splicing, handle splice_init and splice_ack messages, but fail afterwards
1 parent c9a7bfe commit ea76587

File tree

5 files changed

+793
-12
lines changed

5 files changed

+793
-12
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};
@@ -1649,6 +1648,30 @@ impl FundingScope {
16491648
}
16501649
}
16511650

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+
16521675
/// Contains everything about the channel including state, and various flags.
16531676
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
16541677
config: LegacyChannelConfig,
@@ -2290,6 +2313,8 @@ impl<SP: Deref> PendingV2Channel<SP> where SP::Target: SignerProvider {
22902313
context: self.context,
22912314
interactive_tx_signing_session: Some(signing_session),
22922315
holder_commitment_point,
2316+
#[cfg(splicing)]
2317+
pending_splice_pre: None,
22932318
};
22942319
Ok((funded_chan, commitment_signed, funding_ready_for_sig_event))
22952320
},
@@ -4068,6 +4093,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
40684093
}
40694094
}
40704095

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+
40714123
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
40724124
/// number of pending HTLCs that are on track to be in our next commitment tx.
40734125
///
@@ -4538,6 +4590,38 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
45384590
self.channel_transaction_parameters = channel_transaction_parameters;
45394591
self.get_initial_counterparty_commitment_signature(funding, logger)
45404592
}
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+
}
45414625
}
45424626

45434627
// Internal utility functions for channels
@@ -4650,6 +4734,9 @@ pub(super) struct FundedChannel<SP: Deref> where SP::Target: SignerProvider {
46504734
pub context: ChannelContext<SP>,
46514735
pub interactive_tx_signing_session: Option<InteractiveTxSigningSession>,
46524736
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>,
46534740
}
46544741

46554742
#[cfg(any(test, fuzzing))]
@@ -8277,6 +8364,135 @@ impl<SP: Deref> FundedChannel<SP> where
82778364
}
82788365
}
82798366

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

82818497
// Send stuff to our remote peers:
82828498

@@ -9191,6 +9407,8 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
91919407
context: self.context,
91929408
interactive_tx_signing_session: None,
91939409
holder_commitment_point,
9410+
#[cfg(splicing)]
9411+
pending_splice_pre: None,
91949412
};
91959413

91969414
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 {
94589676
context: self.context,
94599677
interactive_tx_signing_session: None,
94609678
holder_commitment_point,
9679+
#[cfg(splicing)]
9680+
pending_splice_pre: None,
94619681
};
94629682
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some()
94639683
|| 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
1084911069
},
1085011070
interactive_tx_signing_session: None,
1085111071
holder_commitment_point,
11072+
#[cfg(splicing)]
11073+
pending_splice_pre: None,
1085211074
})
1085311075
}
1085411076
}
@@ -12667,4 +12889,69 @@ mod tests {
1266712889
320
1266812890
);
1266912891
}
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+
}
1267012957
}

0 commit comments

Comments
 (0)