@@ -109,7 +109,7 @@ use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
109109
110110pub use balance:: { BalanceDetails , LightningBalance , PendingSweepBalance } ;
111111use bitcoin:: secp256k1:: PublicKey ;
112- use bitcoin:: Amount ;
112+ use bitcoin:: { Address , Amount } ;
113113#[ cfg( feature = "uniffi" ) ]
114114pub use builder:: ArcedNodeBuilder as Builder ;
115115pub use builder:: BuildError ;
@@ -1355,6 +1355,72 @@ impl Node {
13551355 }
13561356 }
13571357
1358+ /// Remove funds from an existing channel, sending them to an on-chain address.
1359+ ///
1360+ /// This provides for decreasing a channel's outbound liquidity without re-balancing or closing
1361+ /// it. Once negotiation with the counterparty is complete, the channel remains operational
1362+ /// while waiting for a new funding transaction to confirm.
1363+ ///
1364+ /// # Experimental API
1365+ ///
1366+ /// This API is experimental. Currently, a splice-out will be marked as an inbound payment if
1367+ /// paid to an address associated with the on-chain wallet, but this classification may change
1368+ /// in the future.
1369+ pub fn splice_out (
1370+ & self , user_channel_id : & UserChannelId , counterparty_node_id : PublicKey , address : & Address ,
1371+ splice_amount_sats : u64 ,
1372+ ) -> Result < ( ) , Error > {
1373+ let open_channels =
1374+ self . channel_manager . list_channels_with_counterparty ( & counterparty_node_id) ;
1375+ if let Some ( channel_details) =
1376+ open_channels. iter ( ) . find ( |c| c. user_channel_id == user_channel_id. 0 )
1377+ {
1378+ if splice_amount_sats > channel_details. outbound_capacity_msat {
1379+ return Err ( Error :: ChannelSplicingFailed ) ;
1380+ }
1381+
1382+ self . wallet . parse_and_validate_address ( address) ?;
1383+
1384+ let contribution = SpliceContribution :: SpliceOut {
1385+ outputs : vec ! [ bitcoin:: TxOut {
1386+ value: Amount :: from_sat( splice_amount_sats) ,
1387+ script_pubkey: address. script_pubkey( ) ,
1388+ } ] ,
1389+ } ;
1390+
1391+ let fee_rate = self . fee_estimator . estimate_fee_rate ( ConfirmationTarget :: ChannelFunding ) ;
1392+ let funding_feerate_per_kw: u32 = match fee_rate. to_sat_per_kwu ( ) . try_into ( ) {
1393+ Ok ( fee_rate) => fee_rate,
1394+ Err ( _) => {
1395+ debug_assert ! ( false , "FeeRate should always fit within u32" ) ;
1396+ log_error ! ( self . logger, "FeeRate should always fit within u32" ) ;
1397+ fee_estimator:: get_fallback_rate_for_target ( ConfirmationTarget :: ChannelFunding )
1398+ } ,
1399+ } ;
1400+
1401+ self . channel_manager
1402+ . splice_channel (
1403+ & channel_details. channel_id ,
1404+ & counterparty_node_id,
1405+ contribution,
1406+ funding_feerate_per_kw,
1407+ None ,
1408+ )
1409+ . map_err ( |e| {
1410+ log_error ! ( self . logger, "Failed to splice channel: {:?}" , e) ;
1411+ Error :: ChannelSplicingFailed
1412+ } )
1413+ } else {
1414+ log_error ! (
1415+ self . logger,
1416+ "Channel not found for user_channel_id {} and counterparty {}" ,
1417+ user_channel_id,
1418+ counterparty_node_id
1419+ ) ;
1420+ Err ( Error :: ChannelSplicingFailed )
1421+ }
1422+ }
1423+
13581424 /// Manually sync the LDK and BDK wallets with the current chain state and update the fee rate
13591425 /// cache.
13601426 ///
0 commit comments