Skip to content

Commit c92be83

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

File tree

4 files changed

+462
-11
lines changed

4 files changed

+462
-11
lines changed

lightning/src/ln/channel.rs

+291-2
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};
@@ -1182,6 +1181,30 @@ impl UnfundedChannelContext {
11821181
}
11831182
}
11841183

1184+
/// Info about a pending splice, used in the pre-splice channel
1185+
#[cfg(splicing)]
1186+
#[derive(Clone)]
1187+
struct PendingSplice {
1188+
pub our_funding_contribution: i64,
1189+
}
1190+
1191+
#[cfg(splicing)]
1192+
impl PendingSplice {
1193+
#[inline]
1194+
fn add_checked(base: u64, delta: i64) -> u64 {
1195+
if delta >= 0 {
1196+
base.saturating_add(delta as u64)
1197+
} else {
1198+
base.saturating_sub(delta.abs() as u64)
1199+
}
1200+
}
1201+
1202+
/// Compute the post-splice channel value from the pre-splice values and the peer contributions
1203+
pub fn compute_post_value(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> u64 {
1204+
Self::add_checked(pre_channel_value, our_funding_contribution.saturating_add(their_funding_contribution))
1205+
}
1206+
}
1207+
11851208
/// Contains everything about the channel including state, and various flags.
11861209
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
11871210
config: LegacyChannelConfig,
@@ -3623,6 +3646,33 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
36233646
(context.holder_selected_channel_reserve_satoshis, context.counterparty_selected_channel_reserve_satoshis)
36243647
}
36253648

3649+
/// Check that a balance value meets the channel reserve requirements or violates them (below reserve).
3650+
/// The channel value is an input as opposed to using from self, so that this can be used in case of splicing
3651+
/// to checks with new channel value (before being comitted to it).
3652+
#[cfg(splicing)]
3653+
pub fn check_balance_meets_reserve_requirements(&self, balance: u64, channel_value: u64) -> Result<(), ChannelError> {
3654+
if balance == 0 {
3655+
return Ok(());
3656+
}
3657+
let holder_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
3658+
channel_value, self.holder_dust_limit_satoshis);
3659+
if balance < holder_selected_channel_reserve_satoshis {
3660+
return Err(ChannelError::Warn(format!(
3661+
"Balance below reserve mandated by holder, {} vs {}",
3662+
balance, holder_selected_channel_reserve_satoshis,
3663+
)));
3664+
}
3665+
let counterparty_selected_channel_reserve_satoshis = get_v2_channel_reserve_satoshis(
3666+
channel_value, self.counterparty_dust_limit_satoshis);
3667+
if balance < counterparty_selected_channel_reserve_satoshis {
3668+
return Err(ChannelError::Warn(format!(
3669+
"Balance below reserve mandated by counterparty, {} vs {}",
3670+
balance, counterparty_selected_channel_reserve_satoshis,
3671+
)));
3672+
}
3673+
Ok(())
3674+
}
3675+
36263676
/// Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the
36273677
/// number of pending HTLCs that are on track to be in our next commitment tx.
36283678
///
@@ -4093,6 +4143,38 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
40934143
self.channel_transaction_parameters = channel_transaction_parameters;
40944144
self.get_initial_counterparty_commitment_signature(logger)
40954145
}
4146+
4147+
/// Get the splice message that can be sent during splice initiation.
4148+
#[cfg(splicing)]
4149+
pub fn get_splice_init(&self, our_funding_contribution_satoshis: i64,
4150+
funding_feerate_perkw: u32, locktime: u32,
4151+
) -> msgs::SpliceInit {
4152+
// Reuse the existing funding pubkey, in spite of the channel value changing
4153+
// (though at this point we don't know the new value yet, due tue the optional counterparty contribution)
4154+
// Note that channel_keys_id is supposed NOT to change
4155+
let funding_pubkey = self.get_holder_pubkeys().funding_pubkey.clone();
4156+
msgs::SpliceInit {
4157+
channel_id: self.channel_id,
4158+
funding_contribution_satoshis: our_funding_contribution_satoshis,
4159+
funding_feerate_perkw,
4160+
locktime,
4161+
funding_pubkey,
4162+
require_confirmed_inputs: None,
4163+
}
4164+
}
4165+
4166+
/// Get the splice_ack message that can be sent in response to splice initiation.
4167+
#[cfg(splicing)]
4168+
pub fn get_splice_ack(&self, our_funding_contribution_satoshis: i64) -> msgs::SpliceAck {
4169+
// Reuse the existing funding pubkey, in spite of the channel value changing
4170+
let funding_pubkey = self.get_holder_pubkeys().funding_pubkey;
4171+
msgs::SpliceAck {
4172+
channel_id: self.channel_id,
4173+
funding_contribution_satoshis: our_funding_contribution_satoshis,
4174+
funding_pubkey,
4175+
require_confirmed_inputs: None,
4176+
}
4177+
}
40964178
}
40974179

40984180
// Internal utility functions for channels
@@ -4214,6 +4296,9 @@ pub(super) struct DualFundingChannelContext {
42144296
pub(super) struct Channel<SP: Deref> where SP::Target: SignerProvider {
42154297
pub context: ChannelContext<SP>,
42164298
pub interactive_tx_signing_session: Option<InteractiveTxSigningSession>,
4299+
/// Info about an in-progress, pending splice (if any), on the pre-splice channel
4300+
#[cfg(splicing)]
4301+
pending_splice_pre: Option<PendingSplice>,
42174302
}
42184303

42194304
#[cfg(any(test, fuzzing))]
@@ -7815,6 +7900,135 @@ impl<SP: Deref> Channel<SP> where
78157900
}
78167901
}
78177902

7903+
/// Initiate splicing
7904+
#[cfg(splicing)]
7905+
pub fn splice_channel(&mut self, our_funding_contribution_satoshis: i64,
7906+
our_funding_inputs: Vec<(TxIn, Transaction)>, funding_feerate_perkw: u32, locktime: u32,
7907+
) -> Result<msgs::SpliceInit, ChannelError> {
7908+
// Check if a splice has been initiated already.
7909+
// Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
7910+
if let Some(splice_info) = &self.pending_splice_pre {
7911+
return Err(ChannelError::Warn(format!(
7912+
"Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution
7913+
)));
7914+
}
7915+
7916+
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
7917+
return Err(ChannelError::Warn(format!("Cannot initiate splicing, as channel is not Ready")));
7918+
}
7919+
7920+
let pre_channel_value = self.context.get_value_satoshis();
7921+
// Sanity check: capacity cannot decrease below 0
7922+
if (pre_channel_value as i64).saturating_add(our_funding_contribution_satoshis) < 0 {
7923+
return Err(ChannelError::Warn(format!(
7924+
"Post-splicing channel value cannot be negative. It was {} + {}",
7925+
pre_channel_value, our_funding_contribution_satoshis
7926+
)));
7927+
}
7928+
7929+
if our_funding_contribution_satoshis < 0 {
7930+
return Err(ChannelError::Warn(format!(
7931+
"TODO(splicing): Splice-out not supported, only splice in, contribution {}",
7932+
our_funding_contribution_satoshis,
7933+
)));
7934+
}
7935+
7936+
// Note: post-splice channel value is not yet known at this point, counterpary contribution is not known
7937+
// (Cannot test for miminum required post-splice channel value)
7938+
7939+
// Check that inputs are sufficient to cover our contribution
7940+
let sum_input: i64 = our_funding_inputs.into_iter().map(
7941+
|(txin, tx)| tx.output.get(txin.previous_output.vout as usize).map(|tx| tx.value.to_sat() as i64).unwrap_or(0)
7942+
).sum();
7943+
if sum_input < our_funding_contribution_satoshis {
7944+
return Err(ChannelError::Warn(format!(
7945+
"Provided inputs are insufficient for our contribution, {} {}",
7946+
sum_input, our_funding_contribution_satoshis,
7947+
)));
7948+
}
7949+
7950+
self.pending_splice_pre = Some(PendingSplice {
7951+
our_funding_contribution: our_funding_contribution_satoshis,
7952+
});
7953+
7954+
let msg = self.context.get_splice_init(our_funding_contribution_satoshis, funding_feerate_perkw, locktime);
7955+
Ok(msg)
7956+
}
7957+
7958+
/// Handle splice_init
7959+
#[cfg(splicing)]
7960+
pub fn splice_init(&mut self, msg: &msgs::SpliceInit) -> Result<msgs::SpliceAck, ChannelError> {
7961+
let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
7962+
// TODO(splicing): Currently not possible to contribute on the splicing-acceptor side
7963+
let our_funding_contribution_satoshis = 0i64;
7964+
7965+
// Check if a splice has been initiated already.
7966+
// Note: this could be handled more nicely, and support multiple outstanding splice's, the incoming splice_ack matters anyways.
7967+
if let Some(splice_info) = &self.pending_splice_pre {
7968+
return Err(ChannelError::Warn(format!(
7969+
"Channel has already a splice pending, contribution {}", splice_info.our_funding_contribution,
7970+
)));
7971+
}
7972+
7973+
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
7974+
return Err(ChannelError::Warn(format!("Splicing requested on a channel that is not Ready")));
7975+
}
7976+
7977+
let pre_channel_value = self.context.get_value_satoshis();
7978+
// Sanity check: capacity cannot decrease below 0
7979+
if (pre_channel_value as i64)
7980+
.saturating_add(their_funding_contribution_satoshis)
7981+
.saturating_add(our_funding_contribution_satoshis) < 0
7982+
{
7983+
return Err(ChannelError::Warn(format!(
7984+
"Post-splicing channel value cannot be negative. It was {} + {} + {}",
7985+
pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis,
7986+
)));
7987+
}
7988+
7989+
if their_funding_contribution_satoshis.saturating_add(our_funding_contribution_satoshis) < 0 {
7990+
return Err(ChannelError::Warn(format!(
7991+
"Splice-out not supported, only splice in, relative {} + {}",
7992+
their_funding_contribution_satoshis, our_funding_contribution_satoshis,
7993+
)));
7994+
}
7995+
7996+
let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, their_funding_contribution_satoshis, our_funding_contribution_satoshis);
7997+
let post_balance = PendingSplice::add_checked(self.context.value_to_self_msat, our_funding_contribution_satoshis);
7998+
// Early check for reserve requirement, assuming maximum balance of full channel value
7999+
// This will also be checked later at tx_complete
8000+
let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8001+
8002+
// TODO(splicing): Store msg.funding_pubkey
8003+
// TODO(splicing): Apply start of splice (splice_start)
8004+
8005+
let splice_ack_msg = self.context.get_splice_ack(our_funding_contribution_satoshis);
8006+
// TODO(splicing): start interactive funding negotiation
8007+
Ok(splice_ack_msg)
8008+
}
8009+
8010+
/// Handle splice_ack
8011+
#[cfg(splicing)]
8012+
pub fn splice_ack(&mut self, msg: &msgs::SpliceAck) -> Result<(), ChannelError> {
8013+
let their_funding_contribution_satoshis = msg.funding_contribution_satoshis;
8014+
8015+
// check if splice is pending
8016+
let pending_splice = if let Some(pending_splice) = &self.pending_splice_pre {
8017+
pending_splice
8018+
} else {
8019+
return Err(ChannelError::Warn(format!("Channel is not in pending splice")));
8020+
};
8021+
8022+
let our_funding_contribution = pending_splice.our_funding_contribution;
8023+
8024+
let pre_channel_value = self.context.get_value_satoshis();
8025+
let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution_satoshis);
8026+
let post_balance = PendingSplice::add_checked(self.context.value_to_self_msat, our_funding_contribution);
8027+
// Early check for reserve requirement, assuming maximum balance of full channel value
8028+
// This will also be checked later at tx_complete
8029+
let _res = self.context.check_balance_meets_reserve_requirements(post_balance, post_channel_value)?;
8030+
Ok(())
8031+
}
78188032

78198033
// Send stuff to our remote peers:
78208034

@@ -8491,6 +8705,8 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
84918705
let mut channel = Channel {
84928706
context: self.context,
84938707
interactive_tx_signing_session: None,
8708+
#[cfg(splicing)]
8709+
pending_splice_pre: None,
84948710
};
84958711

84968712
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some();
@@ -8716,6 +8932,8 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
87168932
let mut channel = Channel {
87178933
context: self.context,
87188934
interactive_tx_signing_session: None,
8935+
#[cfg(splicing)]
8936+
pending_splice_pre: None,
87198937
};
87208938
let need_channel_ready = channel.check_get_channel_ready(0, logger).is_some();
87218939
channel.monitor_updating_paused(false, false, need_channel_ready, Vec::new(), Vec::new(), Vec::new());
@@ -8861,6 +9079,8 @@ impl<SP: Deref> OutboundV2Channel<SP> where SP::Target: SignerProvider {
88619079
let channel = Channel {
88629080
context: self.context,
88639081
interactive_tx_signing_session: Some(signing_session),
9082+
#[cfg(splicing)]
9083+
pending_splice_pre: None,
88649084
};
88659085

88669086
Ok(channel)
@@ -9055,6 +9275,8 @@ impl<SP: Deref> InboundV2Channel<SP> where SP::Target: SignerProvider {
90559275
let channel = Channel {
90569276
context: self.context,
90579277
interactive_tx_signing_session: Some(signing_session),
9278+
#[cfg(splicing)]
9279+
pending_splice_pre: None,
90589280
};
90599281

90609282
Ok(channel)
@@ -10133,6 +10355,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
1013310355
next_funding_txid: None,
1013410356
},
1013510357
interactive_tx_signing_session: None,
10358+
#[cfg(splicing)]
10359+
pending_splice_pre: None,
1013610360
})
1013710361
}
1013810362
}
@@ -11915,4 +12139,69 @@ mod tests {
1191512139
assert_eq!(node_a_chan.context.channel_state, ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::THEIR_CHANNEL_READY));
1191612140
assert!(node_a_chan.check_get_channel_ready(0, &&logger).is_some());
1191712141
}
12142+
12143+
#[cfg(all(test, splicing))]
12144+
fn get_pre_and_post(pre_channel_value: u64, our_funding_contribution: i64, their_funding_contribution: i64) -> (u64, u64) {
12145+
use crate::ln::channel::PendingSplice;
12146+
12147+
let post_channel_value = PendingSplice::compute_post_value(pre_channel_value, our_funding_contribution, their_funding_contribution);
12148+
(pre_channel_value, post_channel_value)
12149+
}
12150+
12151+
#[cfg(all(test, splicing))]
12152+
#[test]
12153+
fn test_splice_compute_post_value() {
12154+
{
12155+
// increase, small amounts
12156+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 6_000, 0);
12157+
assert_eq!(pre_channel_value, 9_000);
12158+
assert_eq!(post_channel_value, 15_000);
12159+
}
12160+
{
12161+
// increase, small amounts
12162+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 4_000, 2_000);
12163+
assert_eq!(pre_channel_value, 9_000);
12164+
assert_eq!(post_channel_value, 15_000);
12165+
}
12166+
{
12167+
// increase, small amounts
12168+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, 0, 6_000);
12169+
assert_eq!(pre_channel_value, 9_000);
12170+
assert_eq!(post_channel_value, 15_000);
12171+
}
12172+
{
12173+
// decrease, small amounts
12174+
let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -6_000, 0);
12175+
assert_eq!(pre_channel_value, 15_000);
12176+
assert_eq!(post_channel_value, 9_000);
12177+
}
12178+
{
12179+
// decrease, small amounts
12180+
let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, -4_000, -2_000);
12181+
assert_eq!(pre_channel_value, 15_000);
12182+
assert_eq!(post_channel_value, 9_000);
12183+
}
12184+
{
12185+
// increase and decrease
12186+
let (pre_channel_value, post_channel_value) = get_pre_and_post(15_000, 4_000, -2_000);
12187+
assert_eq!(pre_channel_value, 15_000);
12188+
assert_eq!(post_channel_value, 17_000);
12189+
}
12190+
let base2: u64 = 2;
12191+
let huge63i3 = (base2.pow(63) - 3) as i64;
12192+
assert_eq!(huge63i3, 9223372036854775805);
12193+
assert_eq!(-huge63i3, -9223372036854775805);
12194+
{
12195+
// increase, large amount
12196+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, 3);
12197+
assert_eq!(pre_channel_value, 9_000);
12198+
assert_eq!(post_channel_value, 9223372036854784807);
12199+
}
12200+
{
12201+
// increase, large amounts
12202+
let (pre_channel_value, post_channel_value) = get_pre_and_post(9_000, huge63i3, huge63i3);
12203+
assert_eq!(pre_channel_value, 9_000);
12204+
assert_eq!(post_channel_value, 9223372036854784807);
12205+
}
12206+
}
1191812207
}

0 commit comments

Comments
 (0)