Skip to content

Commit da1c25d

Browse files
authored
Merge pull request #4077 from jkczyz/2025-09-splice-events
Emit `SplicePending` and `SpliceFailed` events
2 parents fcb1164 + 9fee80d commit da1c25d

File tree

10 files changed

+1490
-337
lines changed

10 files changed

+1490
-337
lines changed

lightning/src/events/mod.rs

Lines changed: 146 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ use bitcoin::hashes::sha256::Hash as Sha256;
4949
use bitcoin::hashes::Hash;
5050
use bitcoin::script::ScriptBuf;
5151
use bitcoin::secp256k1::PublicKey;
52-
use bitcoin::{OutPoint, Transaction};
52+
use bitcoin::{OutPoint, Transaction, TxOut};
5353
use core::ops::Deref;
5454

5555
#[allow(unused_imports)]
@@ -1502,6 +1502,71 @@ pub enum Event {
15021502
/// [`ChainMonitor::get_claimable_balances`]: crate::chain::chainmonitor::ChainMonitor::get_claimable_balances
15031503
last_local_balance_msat: Option<u64>,
15041504
},
1505+
/// Used to indicate that a splice for the given `channel_id` has been negotiated and its
1506+
/// funding transaction has been broadcast.
1507+
///
1508+
/// The splice is then considered pending until both parties have seen enough confirmations to
1509+
/// consider the funding locked. Once this occurs, an [`Event::ChannelReady`] will be emitted.
1510+
///
1511+
/// Any UTXOs spent by the splice cannot be reused except by an RBF attempt for the same channel.
1512+
///
1513+
/// # Failure Behavior and Persistence
1514+
/// This event will eventually be replayed after failures-to-handle (i.e., the event handler
1515+
/// returning `Err(ReplayEvent ())`) and will be persisted across restarts.
1516+
SplicePending {
1517+
/// The `channel_id` of the channel that has a pending splice funding transaction.
1518+
channel_id: ChannelId,
1519+
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
1520+
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
1521+
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
1522+
/// `user_channel_id` will be randomized for an inbound channel.
1523+
///
1524+
/// [`ChannelManager::create_channel`]: crate::ln::channelmanager::ChannelManager::create_channel
1525+
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
1526+
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
1527+
user_channel_id: u128,
1528+
/// The `node_id` of the channel counterparty.
1529+
counterparty_node_id: PublicKey,
1530+
/// The outpoint of the channel's splice funding transaction.
1531+
new_funding_txo: OutPoint,
1532+
/// The features that this channel will operate with. Currently, these will be the same
1533+
/// features that the channel was opened with, but in the future splices may change them.
1534+
channel_type: ChannelTypeFeatures,
1535+
},
1536+
/// Used to indicate that a splice for the given `channel_id` has failed.
1537+
///
1538+
/// This event may be emitted if a splice fails after it has been initiated but prior to signing
1539+
/// any negotiated funding transaction.
1540+
///
1541+
/// Any UTXOs contributed to be spent by the funding transaction may be reused and will be
1542+
/// given in `contributed_inputs`.
1543+
///
1544+
/// # Failure Behavior and Persistence
1545+
/// This event will eventually be replayed after failures-to-handle (i.e., the event handler
1546+
/// returning `Err(ReplayEvent ())`) and will be persisted across restarts.
1547+
SpliceFailed {
1548+
/// The `channel_id` of the channel for which the splice failed.
1549+
channel_id: ChannelId,
1550+
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
1551+
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
1552+
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
1553+
/// `user_channel_id` will be randomized for an inbound channel.
1554+
///
1555+
/// [`ChannelManager::create_channel`]: crate::ln::channelmanager::ChannelManager::create_channel
1556+
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
1557+
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
1558+
user_channel_id: u128,
1559+
/// The `node_id` of the channel counterparty.
1560+
counterparty_node_id: PublicKey,
1561+
/// The outpoint of the channel's splice funding transaction, if one was created.
1562+
abandoned_funding_txo: Option<OutPoint>,
1563+
/// The features that this channel will operate with, if available.
1564+
channel_type: Option<ChannelTypeFeatures>,
1565+
/// UTXOs spent as inputs contributed to the splice transaction.
1566+
contributed_inputs: Vec<OutPoint>,
1567+
/// Outputs contributed to the splice transaction.
1568+
contributed_outputs: Vec<TxOut>,
1569+
},
15051570
/// Used to indicate to the user that they can abandon the funding transaction and recycle the
15061571
/// inputs for another purpose.
15071572
///
@@ -2228,6 +2293,42 @@ impl Writeable for Event {
22282293
// We never write out FundingTransactionReadyForSigning events as they will be regenerated when
22292294
// necessary.
22302295
},
2296+
&Event::SplicePending {
2297+
ref channel_id,
2298+
ref user_channel_id,
2299+
ref counterparty_node_id,
2300+
ref new_funding_txo,
2301+
ref channel_type,
2302+
} => {
2303+
50u8.write(writer)?;
2304+
write_tlv_fields!(writer, {
2305+
(1, channel_id, required),
2306+
(3, channel_type, required),
2307+
(5, user_channel_id, required),
2308+
(7, counterparty_node_id, required),
2309+
(9, new_funding_txo, required),
2310+
});
2311+
},
2312+
&Event::SpliceFailed {
2313+
ref channel_id,
2314+
ref user_channel_id,
2315+
ref counterparty_node_id,
2316+
ref abandoned_funding_txo,
2317+
ref channel_type,
2318+
ref contributed_inputs,
2319+
ref contributed_outputs,
2320+
} => {
2321+
52u8.write(writer)?;
2322+
write_tlv_fields!(writer, {
2323+
(1, channel_id, required),
2324+
(3, channel_type, option),
2325+
(5, user_channel_id, required),
2326+
(7, counterparty_node_id, required),
2327+
(9, abandoned_funding_txo, option),
2328+
(11, *contributed_inputs, optional_vec),
2329+
(13, *contributed_outputs, optional_vec),
2330+
});
2331+
},
22312332
// Note that, going forward, all new events must only write data inside of
22322333
// `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
22332334
// data via `write_tlv_fields`.
@@ -2810,6 +2911,50 @@ impl MaybeReadable for Event {
28102911
47u8 => Ok(None),
28112912
// Note that we do not write a length-prefixed TLV for FundingTransactionReadyForSigning events.
28122913
49u8 => Ok(None),
2914+
50u8 => {
2915+
let mut f = || {
2916+
_init_and_read_len_prefixed_tlv_fields!(reader, {
2917+
(1, channel_id, required),
2918+
(3, channel_type, required),
2919+
(5, user_channel_id, required),
2920+
(7, counterparty_node_id, required),
2921+
(9, new_funding_txo, required),
2922+
});
2923+
2924+
Ok(Some(Event::SplicePending {
2925+
channel_id: channel_id.0.unwrap(),
2926+
user_channel_id: user_channel_id.0.unwrap(),
2927+
counterparty_node_id: counterparty_node_id.0.unwrap(),
2928+
new_funding_txo: new_funding_txo.0.unwrap(),
2929+
channel_type: channel_type.0.unwrap(),
2930+
}))
2931+
};
2932+
f()
2933+
},
2934+
52u8 => {
2935+
let mut f = || {
2936+
_init_and_read_len_prefixed_tlv_fields!(reader, {
2937+
(1, channel_id, required),
2938+
(3, channel_type, option),
2939+
(5, user_channel_id, required),
2940+
(7, counterparty_node_id, required),
2941+
(9, abandoned_funding_txo, option),
2942+
(11, contributed_inputs, optional_vec),
2943+
(13, contributed_outputs, optional_vec),
2944+
});
2945+
2946+
Ok(Some(Event::SpliceFailed {
2947+
channel_id: channel_id.0.unwrap(),
2948+
user_channel_id: user_channel_id.0.unwrap(),
2949+
counterparty_node_id: counterparty_node_id.0.unwrap(),
2950+
abandoned_funding_txo,
2951+
channel_type,
2952+
contributed_inputs: contributed_inputs.unwrap_or_default(),
2953+
contributed_outputs: contributed_outputs.unwrap_or_default(),
2954+
}))
2955+
};
2956+
f()
2957+
},
28132958
// Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
28142959
// Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt
28152960
// reads.

0 commit comments

Comments
 (0)