@@ -110,7 +110,7 @@ use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
110110use crate :: scoring:: setup_background_pathfinding_scores_sync;
111111pub use balance:: { BalanceDetails , LightningBalance , PendingSweepBalance } ;
112112use bitcoin:: secp256k1:: PublicKey ;
113- use bitcoin:: Amount ;
113+ use bitcoin:: { Address , Amount } ;
114114#[ cfg( feature = "uniffi" ) ]
115115pub use builder:: ArcedNodeBuilder as Builder ;
116116pub use builder:: BuildError ;
@@ -1328,6 +1328,59 @@ impl Node {
13281328 }
13291329 }
13301330
1331+ /// Remove funds from an existing channel, sending them to an on-chain address.
1332+ ///
1333+ /// This provides for decreasing a channel's outbound liquidity without re-balancing or closing
1334+ /// it. Once negotiation with the counterparty is complete, the channel remains operational
1335+ /// while waiting for a new funding transaction to confirm.
1336+ pub fn splice_out (
1337+ & self , user_channel_id : & UserChannelId , counterparty_node_id : PublicKey , address : Address ,
1338+ splice_amount_sats : u64 ,
1339+ ) -> Result < ( ) , Error > {
1340+ let open_channels =
1341+ self . channel_manager . list_channels_with_counterparty ( & counterparty_node_id) ;
1342+ if let Some ( channel_details) =
1343+ open_channels. iter ( ) . find ( |c| c. user_channel_id == user_channel_id. 0 )
1344+ {
1345+ if splice_amount_sats > channel_details. outbound_capacity_msat {
1346+ return Err ( Error :: ChannelSplicingFailed ) ;
1347+ }
1348+
1349+ self . wallet . parse_and_validate_address ( & address) ?;
1350+
1351+ let contribution = SpliceContribution :: SpliceOut {
1352+ outputs : vec ! [ bitcoin:: TxOut {
1353+ value: Amount :: from_sat( splice_amount_sats) ,
1354+ script_pubkey: address. script_pubkey( ) ,
1355+ } ] ,
1356+ } ;
1357+
1358+ let fee_rate = self . wallet . estimate_channel_funding_fee_rate ( ) ;
1359+ let funding_feerate_per_kw = fee_rate. to_sat_per_kwu ( ) . try_into ( ) . unwrap_or ( u32:: MAX ) ;
1360+
1361+ self . channel_manager
1362+ . splice_channel (
1363+ & channel_details. channel_id ,
1364+ & counterparty_node_id,
1365+ contribution,
1366+ funding_feerate_per_kw,
1367+ None ,
1368+ )
1369+ . map_err ( |e| {
1370+ log_error ! ( self . logger, "Failed to splice channel: {:?}" , e) ;
1371+ Error :: ChannelSplicingFailed
1372+ } )
1373+ } else {
1374+ log_error ! (
1375+ self . logger,
1376+ "Channel not found for user_channel_id: {:?} and counterparty: {}" ,
1377+ user_channel_id,
1378+ counterparty_node_id
1379+ ) ;
1380+ Err ( Error :: ChannelSplicingFailed )
1381+ }
1382+ }
1383+
13311384 /// Manually sync the LDK and BDK wallets with the current chain state and update the fee rate
13321385 /// cache.
13331386 ///
0 commit comments