@@ -28,7 +28,7 @@ use lightning_invoice::RawBolt11Invoice;
2828
2929use bdk_chain:: spk_client:: { FullScanRequest , SyncRequest } ;
3030use bdk_chain:: ChainPosition ;
31- use bdk_wallet:: { KeychainKind , PersistedWallet , SignOptions , Update } ;
31+ use bdk_wallet:: { Balance , KeychainKind , PersistedWallet , SignOptions , Update } ;
3232
3333use bitcoin:: blockdata:: constants:: WITNESS_SCALE_FACTOR ;
3434use bitcoin:: blockdata:: locktime:: absolute:: LockTime ;
@@ -45,6 +45,12 @@ use bitcoin::{
4545use std:: ops:: Deref ;
4646use std:: sync:: { Arc , Mutex } ;
4747
48+ pub ( crate ) enum OnchainSendType {
49+ SendRetainingReserve { amount_sats : u64 , cur_anchor_reserve_sats : u64 } ,
50+ SendAllRetainingReserve { cur_anchor_reserve_sats : u64 } ,
51+ SendAllDrainingReserve ,
52+ }
53+
4854pub ( crate ) mod persist;
4955pub ( crate ) mod ser;
5056
@@ -215,6 +221,12 @@ where
215221 ) ;
216222 }
217223
224+ self . get_balances_inner ( balance, total_anchor_channels_reserve_sats)
225+ }
226+
227+ fn get_balances_inner (
228+ & self , balance : Balance , total_anchor_channels_reserve_sats : u64 ,
229+ ) -> Result < ( u64 , u64 ) , Error > {
218230 let ( total, spendable) = (
219231 balance. total ( ) . to_sat ( ) ,
220232 balance. trusted_spendable ( ) . to_sat ( ) . saturating_sub ( total_anchor_channels_reserve_sats) ,
@@ -229,32 +241,95 @@ where
229241 self . get_balances ( total_anchor_channels_reserve_sats) . map ( |( _, s) | s)
230242 }
231243
232- /// Send funds to the given address.
233- ///
234- /// If `amount_msat_or_drain` is `None` the wallet will be drained, i.e., all available funds will be
235- /// spent.
236244 pub ( crate ) fn send_to_address (
237- & self , address : & bitcoin:: Address , amount_or_drain : Option < Amount > ,
245+ & self , address : & bitcoin:: Address , send_amount : OnchainSendType ,
238246 ) -> Result < Txid , Error > {
239247 let confirmation_target = ConfirmationTarget :: OnchainPayment ;
240248 let fee_rate = self . fee_estimator . estimate_fee_rate ( confirmation_target) ;
241249
242250 let tx = {
243251 let mut locked_wallet = self . inner . lock ( ) . unwrap ( ) ;
244- let mut tx_builder = locked_wallet. build_tx ( ) ;
245-
246- if let Some ( amount) = amount_or_drain {
247- tx_builder
248- . add_recipient ( address. script_pubkey ( ) , amount)
249- . fee_rate ( fee_rate)
250- . enable_rbf ( ) ;
251- } else {
252- tx_builder
253- . drain_wallet ( )
254- . drain_to ( address. script_pubkey ( ) )
255- . fee_rate ( fee_rate)
256- . enable_rbf ( ) ;
257- }
252+
253+ // Prepare the tx_builder. We properly check the reserve requirements (again) further down.
254+ let tx_builder = match send_amount {
255+ OnchainSendType :: SendRetainingReserve { amount_sats, .. } => {
256+ let mut tx_builder = locked_wallet. build_tx ( ) ;
257+ let amount = Amount :: from_sat ( amount_sats) ;
258+ tx_builder
259+ . add_recipient ( address. script_pubkey ( ) , amount)
260+ . fee_rate ( fee_rate)
261+ . enable_rbf ( ) ;
262+ tx_builder
263+ } ,
264+ OnchainSendType :: SendAllRetainingReserve { cur_anchor_reserve_sats } => {
265+ let change_address_info = locked_wallet. peek_address ( KeychainKind :: Internal , 0 ) ;
266+ let balance = locked_wallet. balance ( ) ;
267+ let spendable_amount_sats = self
268+ . get_balances_inner ( balance, cur_anchor_reserve_sats)
269+ . map ( |( _, s) | s)
270+ . unwrap_or ( 0 ) ;
271+ let tmp_tx = {
272+ let mut tmp_tx_builder = locked_wallet. build_tx ( ) ;
273+ tmp_tx_builder
274+ . drain_wallet ( )
275+ . drain_to ( address. script_pubkey ( ) )
276+ . add_recipient (
277+ change_address_info. address . script_pubkey ( ) ,
278+ Amount :: from_sat ( cur_anchor_reserve_sats) ,
279+ )
280+ . fee_rate ( fee_rate)
281+ . enable_rbf ( ) ;
282+ match tmp_tx_builder. finish ( ) {
283+ Ok ( psbt) => psbt. unsigned_tx ,
284+ Err ( err) => {
285+ log_error ! (
286+ self . logger,
287+ "Failed to create temporary transaction: {}" ,
288+ err
289+ ) ;
290+ return Err ( err. into ( ) ) ;
291+ } ,
292+ }
293+ } ;
294+
295+ let estimated_tx_fee = locked_wallet. calculate_fee ( & tmp_tx) . map_err ( |e| {
296+ log_error ! (
297+ self . logger,
298+ "Failed to calculate fee of temporary transaction: {}" ,
299+ e
300+ ) ;
301+ e
302+ } ) ?;
303+ let estimated_spendable_amount = Amount :: from_sat (
304+ spendable_amount_sats. saturating_sub ( estimated_tx_fee. to_sat ( ) ) ,
305+ ) ;
306+
307+ if estimated_spendable_amount == Amount :: ZERO {
308+ log_error ! ( self . logger,
309+ "Unable to send payment without infringing on Anchor reserves. Available: {}sats, estimated fee required: {}sats." ,
310+ spendable_amount_sats,
311+ estimated_tx_fee,
312+ ) ;
313+ return Err ( Error :: InsufficientFunds ) ;
314+ }
315+
316+ let mut tx_builder = locked_wallet. build_tx ( ) ;
317+ tx_builder
318+ . add_recipient ( address. script_pubkey ( ) , estimated_spendable_amount)
319+ . fee_absolute ( estimated_tx_fee)
320+ . enable_rbf ( ) ;
321+ tx_builder
322+ } ,
323+ OnchainSendType :: SendAllDrainingReserve => {
324+ let mut tx_builder = locked_wallet. build_tx ( ) ;
325+ tx_builder
326+ . drain_wallet ( )
327+ . drain_to ( address. script_pubkey ( ) )
328+ . fee_rate ( fee_rate)
329+ . enable_rbf ( ) ;
330+ tx_builder
331+ } ,
332+ } ;
258333
259334 let mut psbt = match tx_builder. finish ( ) {
260335 Ok ( psbt) => {
@@ -267,6 +342,55 @@ where
267342 } ,
268343 } ;
269344
345+ // Check the reserve requirements (again) and return an error if they aren't met.
346+ match send_amount {
347+ OnchainSendType :: SendRetainingReserve { amount_sats, cur_anchor_reserve_sats } => {
348+ let balance = locked_wallet. balance ( ) ;
349+ let spendable_amount_sats = self
350+ . get_balances_inner ( balance, cur_anchor_reserve_sats)
351+ . map ( |( _, s) | s)
352+ . unwrap_or ( 0 ) ;
353+ let tx_fee_sats = locked_wallet
354+ . calculate_fee ( & psbt. unsigned_tx )
355+ . map_err ( |e| {
356+ log_error ! (
357+ self . logger,
358+ "Failed to calculate fee of candidate transaction: {}" ,
359+ e
360+ ) ;
361+ e
362+ } ) ?
363+ . to_sat ( ) ;
364+ if spendable_amount_sats < amount_sats + tx_fee_sats {
365+ log_error ! ( self . logger,
366+ "Unable to send payment due to insufficient funds. Available: {}sats, Required: {}sats + {}sats fee" ,
367+ spendable_amount_sats,
368+ amount_sats,
369+ tx_fee_sats,
370+ ) ;
371+ return Err ( Error :: InsufficientFunds ) ;
372+ }
373+ } ,
374+ OnchainSendType :: SendAllRetainingReserve { cur_anchor_reserve_sats } => {
375+ let balance = locked_wallet. balance ( ) ;
376+ let spendable_amount_sats = self
377+ . get_balances_inner ( balance, cur_anchor_reserve_sats)
378+ . map ( |( _, s) | s)
379+ . unwrap_or ( 0 ) ;
380+ let ( sent, received) = locked_wallet. sent_and_received ( & psbt. unsigned_tx ) ;
381+ let drain_amount = sent - received;
382+ if spendable_amount_sats < drain_amount. to_sat ( ) {
383+ log_error ! ( self . logger,
384+ "Unable to send payment due to insufficient funds. Available: {}sats, Required: {}" ,
385+ spendable_amount_sats,
386+ drain_amount,
387+ ) ;
388+ return Err ( Error :: InsufficientFunds ) ;
389+ }
390+ } ,
391+ _ => { } ,
392+ }
393+
270394 match locked_wallet. sign ( & mut psbt, SignOptions :: default ( ) ) {
271395 Ok ( finalized) => {
272396 if !finalized {
@@ -295,21 +419,33 @@ where
295419
296420 let txid = tx. compute_txid ( ) ;
297421
298- if let Some ( amount) = amount_or_drain {
299- log_info ! (
300- self . logger,
301- "Created new transaction {} sending {}sats on-chain to address {}" ,
302- txid,
303- amount. to_sat( ) ,
304- address
305- ) ;
306- } else {
307- log_info ! (
308- self . logger,
309- "Created new transaction {} sending all available on-chain funds to address {}" ,
310- txid,
311- address
312- ) ;
422+ match send_amount {
423+ OnchainSendType :: SendRetainingReserve { amount_sats, .. } => {
424+ log_info ! (
425+ self . logger,
426+ "Created new transaction {} sending {}sats on-chain to address {}" ,
427+ txid,
428+ amount_sats,
429+ address
430+ ) ;
431+ } ,
432+ OnchainSendType :: SendAllRetainingReserve { cur_anchor_reserve_sats } => {
433+ log_info ! (
434+ self . logger,
435+ "Created new transaction {} sending available on-chain funds retaining a reserve of {}sats to address {}" ,
436+ txid,
437+ cur_anchor_reserve_sats,
438+ address,
439+ ) ;
440+ } ,
441+ OnchainSendType :: SendAllDrainingReserve => {
442+ log_info ! (
443+ self . logger,
444+ "Created new transaction {} sending all available on-chain funds to address {}" ,
445+ txid,
446+ address
447+ ) ;
448+ } ,
313449 }
314450
315451 Ok ( txid)
0 commit comments