Skip to content

Commit 3451d6f

Browse files
committed
Partial, handle splice_init & splice_ack messages #3407
2 parents c9a7bfe + bbc3c65 commit 3451d6f

File tree

8 files changed

+842
-25
lines changed

8 files changed

+842
-25
lines changed

lightning/src/ln/chan_utils.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,27 @@ pub const HTLC_TIMEOUT_INPUT_ANCHOR_WITNESS_WEIGHT: u64 = 288;
8181
/// outputs.
8282
pub const HTLC_SUCCESS_INPUT_ANCHOR_WITNESS_WEIGHT: u64 = 327;
8383

84+
/// The size of the 2-of-2 multisig script
85+
const MULTISIG_SCRIPT_SIZE: u64 =
86+
1 + // OP_2
87+
1 + // data len
88+
33 + // pubkey1
89+
1 + // data len
90+
33 + // pubkey2
91+
1 + // OP_2
92+
1; // OP_CHECKMULTISIG
93+
/// The weight of a funding transaction input (2-of-2 P2WSH)
94+
/// See https://github.com/lightning/bolts/blob/master/03-transactions.md#expected-weight-of-the-commitment-transaction
95+
pub const FUNDING_TRANSACTION_WITNESS_WEIGHT: u64 =
96+
1 + // number_of_witness_elements
97+
1 + // nil_len
98+
1 + // sig len
99+
73 + // sig1
100+
1 + // sig len
101+
73 + // sig2
102+
1 + // witness_script_length
103+
MULTISIG_SCRIPT_SIZE;
104+
84105
/// Gets the weight for an HTLC-Success transaction.
85106
#[inline]
86107
pub fn htlc_success_tx_weight(channel_type_features: &ChannelTypeFeatures) -> u64 {

lightning/src/ln/channel.rs

Lines changed: 449 additions & 2 deletions
Large diffs are not rendered by default.

lightning/src/ln/channelmanager.rs

Lines changed: 184 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ use bitcoin::hash_types::{BlockHash, Txid};
3131
use bitcoin::secp256k1::{SecretKey,PublicKey};
3232
use bitcoin::secp256k1::Secp256k1;
3333
use bitcoin::{secp256k1, Sequence};
34+
#[cfg(splicing)]
35+
use bitcoin::{TxIn, Weight};
36+
3437

3538
use crate::events::FundingInfo;
3639
use crate::blinded_path::message::{AsyncPaymentsContext, MessageContext, OffersContext};
@@ -49,7 +52,7 @@ use crate::ln::inbound_payment;
4952
use crate::ln::types::ChannelId;
5053
use crate::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
5154
use crate::ln::channel::{self, Channel, ChannelError, ChannelUpdateStatus, FundedChannel, ShutdownResult, UpdateFulfillCommitFetch, OutboundV1Channel, ReconnectionMsg, InboundV1Channel, WithChannelContext};
52-
#[cfg(any(dual_funding, splicing))]
55+
#[cfg(dual_funding)]
5356
use crate::ln::channel::PendingV2Channel;
5457
use crate::ln::channel_state::ChannelDetails;
5558
use crate::types::features::{Bolt12InvoiceFeatures, ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
@@ -4278,6 +4281,88 @@ where
42784281
}
42794282
}
42804283

4284+
/// See [`splice_channel`]
4285+
#[cfg(splicing)]
4286+
fn internal_splice_channel(
4287+
&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, our_funding_contribution_satoshis: i64,
4288+
our_funding_inputs: &Vec<(TxIn, Transaction, Weight)>,
4289+
funding_feerate_per_kw: u32, locktime: Option<u32>,
4290+
) -> (Result<(), APIError>, NotifyOption) {
4291+
let per_peer_state = self.per_peer_state.read().unwrap();
4292+
4293+
let peer_state_mutex = match per_peer_state.get(counterparty_node_id)
4294+
.ok_or_else(|| APIError::ChannelUnavailable { err: format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id) }) {
4295+
Ok(p) => p,
4296+
Err(e) => return (Err(e), NotifyOption::SkipPersistNoEvents),
4297+
};
4298+
4299+
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
4300+
let peer_state = &mut *peer_state_lock;
4301+
4302+
// Look for the channel
4303+
match peer_state.channel_by_id.entry(*channel_id) {
4304+
hash_map::Entry::Occupied(mut chan_phase_entry) => {
4305+
let locktime = locktime.unwrap_or(self.current_best_block().height);
4306+
if let Some(chan) = chan_phase_entry.get_mut().as_funded_mut() {
4307+
let msg = match chan.splice_channel(our_funding_contribution_satoshis, our_funding_inputs, funding_feerate_per_kw, locktime) {
4308+
Ok(m) => m,
4309+
Err(e) => return (Err(e), NotifyOption::SkipPersistNoEvents),
4310+
};
4311+
4312+
peer_state.pending_msg_events.push(events::MessageSendEvent::SendSpliceInit {
4313+
node_id: *counterparty_node_id,
4314+
msg,
4315+
});
4316+
4317+
(Ok(()), NotifyOption::SkipPersistHandleEvents)
4318+
} else {
4319+
(Err(APIError::ChannelUnavailable {
4320+
err: format!(
4321+
"Channel with id {} is not funded, cannot splice it",
4322+
channel_id
4323+
)
4324+
}), NotifyOption::SkipPersistNoEvents)
4325+
}
4326+
},
4327+
hash_map::Entry::Vacant(_) => {
4328+
(Err(APIError::ChannelUnavailable {
4329+
err: format!(
4330+
"Channel with id {} not found for the passed counterparty node_id {}",
4331+
channel_id, counterparty_node_id,
4332+
)
4333+
}), NotifyOption::SkipPersistNoEvents)
4334+
},
4335+
}
4336+
}
4337+
4338+
/// Initiate a splice, to change the channel capacity of an existing funded channel.
4339+
/// After completion of splicing, the funding transaction will be replaced by a new one, spending the old funding transaction,
4340+
/// with optional extra inputs (splice-in) and/or extra outputs (splice-out or change).
4341+
/// TODO(splicing): Implementation is currently incomplete.
4342+
///
4343+
/// Note: Currently only splice-in is supported (increase in channel capacity), splice-out is not.
4344+
///
4345+
/// - our_funding_contribution_satoshis: the amount contributed by us to the channel. This will increase our channel balance.
4346+
/// - our_funding_inputs: the funding inputs provided by us. If our contribution is positive, our funding inputs must cover at least that amount.
4347+
/// Includes the witness weight for this input (e.g. P2WPKH_WITNESS_WEIGHT=109 for typical P2WPKH inputs).
4348+
/// - locktime: Optional locktime for the new funding transaction. If None, set to the current block height.
4349+
#[cfg(splicing)]
4350+
pub fn splice_channel(
4351+
&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, our_funding_contribution_satoshis: i64,
4352+
our_funding_inputs: Vec<(TxIn, Transaction, Weight)>,
4353+
funding_feerate_per_kw: u32, locktime: Option<u32>,
4354+
) -> Result<(), APIError> {
4355+
let mut res = Ok(());
4356+
PersistenceNotifierGuard::optionally_notify(self, || {
4357+
let (result, notify_option) = self.internal_splice_channel(
4358+
channel_id, counterparty_node_id, our_funding_contribution_satoshis, &our_funding_inputs, funding_feerate_per_kw, locktime
4359+
);
4360+
res = result;
4361+
notify_option
4362+
});
4363+
res
4364+
}
4365+
42814366
fn can_forward_htlc_to_outgoing_channel(
42824367
&self, chan: &mut FundedChannel<SP>, msg: &msgs::UpdateAddHTLC, next_packet: &NextPacketDetails
42834368
) -> Result<(), (&'static str, u16)> {
@@ -9466,6 +9551,83 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
94669551
Ok(NotifyOption::SkipPersistHandleEvents)
94679552
}
94689553

9554+
/// Handle incoming splice request, transition channel to splice-pending (unless some check fails).
9555+
#[cfg(splicing)]
9556+
fn internal_splice_init(&self, counterparty_node_id: &PublicKey, msg: &msgs::SpliceInit) -> Result<(), MsgHandleErrInternal> {
9557+
// TODO(splicing): if we accept splicing, quiescence
9558+
9559+
let per_peer_state = self.per_peer_state.read().unwrap();
9560+
let peer_state_mutex = per_peer_state.get(counterparty_node_id)
9561+
.ok_or_else(|| {
9562+
debug_assert!(false);
9563+
MsgHandleErrInternal::send_err_msg_no_close(format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id), msg.channel_id)
9564+
})?;
9565+
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
9566+
let peer_state = &mut *peer_state_lock;
9567+
9568+
// Look for the channel
9569+
match peer_state.channel_by_id.entry(msg.channel_id) {
9570+
hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close(format!(
9571+
"Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}, channel_id {}",
9572+
counterparty_node_id, msg.channel_id,
9573+
), msg.channel_id)),
9574+
hash_map::Entry::Occupied(mut chan_entry) => {
9575+
if let Some(chan) = chan_entry.get_mut().as_funded_mut() {
9576+
let splice_ack_msg = try_channel_entry!(self, peer_state, chan.splice_init(msg), chan_entry);
9577+
peer_state.pending_msg_events.push(events::MessageSendEvent::SendSpliceAck {
9578+
node_id: *counterparty_node_id,
9579+
msg: splice_ack_msg,
9580+
});
9581+
} else {
9582+
return Err(MsgHandleErrInternal::send_err_msg_no_close("Channel is not funded, cannot be spliced".to_owned(), msg.channel_id));
9583+
}
9584+
},
9585+
};
9586+
9587+
// TODO(splicing):
9588+
// Change channel, change phase (remove and add)
9589+
// Create new post-splice channel
9590+
// etc.
9591+
9592+
Ok(())
9593+
}
9594+
9595+
/// Handle incoming splice request ack, transition channel to splice-pending (unless some check fails).
9596+
#[cfg(splicing)]
9597+
fn internal_splice_ack(&self, counterparty_node_id: &PublicKey, msg: &msgs::SpliceAck) -> Result<(), MsgHandleErrInternal> {
9598+
let per_peer_state = self.per_peer_state.read().unwrap();
9599+
let peer_state_mutex = per_peer_state.get(counterparty_node_id)
9600+
.ok_or_else(|| {
9601+
debug_assert!(false);
9602+
MsgHandleErrInternal::send_err_msg_no_close(format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id), msg.channel_id)
9603+
})?;
9604+
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
9605+
let peer_state = &mut *peer_state_lock;
9606+
9607+
// Look for the channel
9608+
match peer_state.channel_by_id.entry(msg.channel_id) {
9609+
hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close(format!(
9610+
"Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}",
9611+
counterparty_node_id
9612+
), msg.channel_id)),
9613+
hash_map::Entry::Occupied(mut chan_entry) => {
9614+
if let Some(chan) = chan_entry.get_mut().as_funded_mut() {
9615+
try_channel_entry!(self, peer_state, chan.splice_ack(msg), chan_entry);
9616+
} else {
9617+
return Err(MsgHandleErrInternal::send_err_msg_no_close("Channel is not funded, cannot splice".to_owned(), msg.channel_id));
9618+
}
9619+
},
9620+
};
9621+
9622+
// TODO(splicing):
9623+
// Change channel, change phase (remove and add)
9624+
// Create new post-splice channel
9625+
// Start splice funding transaction negotiation
9626+
// etc.
9627+
9628+
Err(MsgHandleErrInternal::send_err_msg_no_close("TODO(splicing): Splicing is not implemented (splice_ack)".to_owned(), msg.channel_id))
9629+
}
9630+
94699631
/// Process pending events from the [`chain::Watch`], returning whether any events were processed.
94709632
fn process_pending_monitor_events(&self) -> bool {
94719633
debug_assert!(self.total_consistency_lock.try_write().is_err()); // Caller holds read lock
@@ -11746,23 +11908,37 @@ where
1174611908

1174711909
#[cfg(splicing)]
1174811910
fn handle_splice_init(&self, counterparty_node_id: PublicKey, msg: &msgs::SpliceInit) {
11749-
let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
11750-
"Splicing not supported".to_owned(),
11751-
msg.channel_id.clone())), counterparty_node_id);
11911+
let _persistence_guard = PersistenceNotifierGuard::optionally_notify(self, || {
11912+
let res = self.internal_splice_init(&counterparty_node_id, msg);
11913+
let persist = match &res {
11914+
Err(e) if e.closes_channel() => NotifyOption::DoPersist,
11915+
Err(_) => NotifyOption::SkipPersistHandleEvents,
11916+
Ok(()) => NotifyOption::SkipPersistHandleEvents,
11917+
};
11918+
let _ = handle_error!(self, res, counterparty_node_id);
11919+
persist
11920+
});
1175211921
}
1175311922

1175411923
#[cfg(splicing)]
1175511924
fn handle_splice_ack(&self, counterparty_node_id: PublicKey, msg: &msgs::SpliceAck) {
11756-
let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
11757-
"Splicing not supported (splice_ack)".to_owned(),
11758-
msg.channel_id.clone())), counterparty_node_id);
11925+
let _persistence_guard = PersistenceNotifierGuard::optionally_notify(self, || {
11926+
let res = self.internal_splice_ack(&counterparty_node_id, msg);
11927+
let persist = match &res {
11928+
Err(e) if e.closes_channel() => NotifyOption::DoPersist,
11929+
Err(_) => NotifyOption::SkipPersistHandleEvents,
11930+
Ok(()) => NotifyOption::SkipPersistHandleEvents,
11931+
};
11932+
let _ = handle_error!(self, res, counterparty_node_id);
11933+
persist
11934+
});
1175911935
}
1176011936

1176111937
#[cfg(splicing)]
1176211938
fn handle_splice_locked(&self, counterparty_node_id: PublicKey, msg: &msgs::SpliceLocked) {
1176311939
let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
1176411940
"Splicing not supported (splice_locked)".to_owned(),
11765-
msg.channel_id.clone())), counterparty_node_id);
11941+
msg.channel_id)), counterparty_node_id);
1176611942
}
1176711943

1176811944
fn handle_shutdown(&self, counterparty_node_id: PublicKey, msg: &msgs::Shutdown) {

lightning/src/ln/dual_funding_tests.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,16 @@ fn do_test_v2_channel_establishment(
4949
let logger_a = test_utils::TestLogger::with_id("node a".to_owned());
5050

5151
// Create a funding input for the new channel along with its previous transaction.
52-
let initiator_funding_inputs: Vec<_> = create_dual_funding_utxos_with_prev_txs(
52+
let initiator_funding_inputs = create_dual_funding_utxos_with_prev_txs(
5353
&nodes[0],
5454
&[session.initiator_input_value_satoshis],
55-
)
56-
.into_iter()
57-
.map(|(txin, tx)| (txin, TransactionU16LenLimited::new(tx).unwrap()))
58-
.collect();
55+
);
56+
let total_weight =
57+
Weight::from_wu(initiator_funding_inputs.iter().map(|(_, _, w)| w.to_wu()).sum());
58+
let initiator_funding_inputs: Vec<_> = initiator_funding_inputs
59+
.into_iter()
60+
.map(|(txin, tx, _)| (txin, TransactionU16LenLimited::new(tx).unwrap()))
61+
.collect();
5962

6063
// Alice creates a dual-funded channel as initiator.
6164
let funding_satoshis = session.funding_input_sats;

lightning/src/ln/functional_test_utils.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use crate::util::test_utils;
3838
use crate::util::test_utils::{TestChainMonitor, TestScorer, TestKeysInterface};
3939
use crate::util::ser::{ReadableArgs, Writeable};
4040

41-
use bitcoin::WPubkeyHash;
41+
use bitcoin::{Weight, WPubkeyHash};
4242
use bitcoin::amount::Amount;
4343
use bitcoin::block::{Block, Header, Version as BlockVersion};
4444
use bitcoin::locktime::absolute::{LockTime, LOCK_TIME_THRESHOLD};
@@ -60,6 +60,7 @@ use core::mem;
6060
use core::ops::Deref;
6161
use crate::io;
6262
use crate::prelude::*;
63+
use crate::sign::P2WPKH_WITNESS_WEIGHT;
6364
use crate::sync::{Arc, Mutex, LockTestExt, RwLock};
6465

6566
pub const CHAN_CONFIRM_DEPTH: u32 = 10;
@@ -804,7 +805,7 @@ macro_rules! get_event_msg {
804805
assert_eq!(*node_id, $node_id);
805806
(*msg).clone()
806807
},
807-
_ => panic!("Unexpected event"),
808+
_ => panic!("Unexpected event {:?}", events[0]),
808809
}
809810
}
810811
}
@@ -1236,9 +1237,11 @@ fn internal_create_funding_transaction<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>,
12361237
}
12371238
}
12381239

1240+
/// Create test inputs for a funding transaction.
1241+
/// Return the inputs (with prev tx), and the total witness weight for these inputs
12391242
pub fn create_dual_funding_utxos_with_prev_txs(
12401243
node: &Node<'_, '_, '_>, utxo_values_in_satoshis: &[u64],
1241-
) -> Vec<(TxIn, Transaction)> {
1244+
) -> Vec<(TxIn, Transaction, Weight)> {
12421245
// Ensure we have unique transactions per node by using the locktime.
12431246
let tx = Transaction {
12441247
version: TxVersion::TWO,
@@ -1251,9 +1254,9 @@ pub fn create_dual_funding_utxos_with_prev_txs(
12511254
}).collect()
12521255
};
12531256

1254-
let mut result = vec![];
1257+
let mut inputs = vec![];
12551258
for i in 0..utxo_values_in_satoshis.len() {
1256-
result.push(
1259+
inputs.push(
12571260
(TxIn {
12581261
previous_output: OutPoint {
12591262
txid: tx.compute_txid(),
@@ -1262,9 +1265,13 @@ pub fn create_dual_funding_utxos_with_prev_txs(
12621265
script_sig: ScriptBuf::new(),
12631266
sequence: Sequence::ZERO,
12641267
witness: Witness::new(),
1265-
}, tx.clone()));
1268+
},
1269+
tx.clone(),
1270+
Weight::from_wu(P2WPKH_WITNESS_WEIGHT),
1271+
));
12661272
}
1267-
result
1273+
1274+
inputs
12681275
}
12691276

12701277
pub fn sign_funding_transaction<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, 'b, 'c>, channel_value: u64, expected_temporary_channel_id: ChannelId) -> Transaction {

0 commit comments

Comments
 (0)