@@ -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 OnchainSendAmount {
49+ ExactRetainingReserve { amount_sats : u64 , cur_anchor_reserve_sats : u64 } ,
50+ AllRetainingReserve { cur_anchor_reserve_sats : u64 } ,
51+ AllDrainingReserve ,
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 : OnchainSendAmount ,
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+ OnchainSendAmount :: ExactRetainingReserve { 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+ OnchainSendAmount :: AllRetainingReserve { 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+ OnchainSendAmount :: AllDrainingReserve => {
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,58 @@ where
267342 } ,
268343 } ;
269344
345+ // Check the reserve requirements (again) and return an error if they aren't met.
346+ match send_amount {
347+ OnchainSendAmount :: ExactRetainingReserve {
348+ amount_sats,
349+ cur_anchor_reserve_sats,
350+ } => {
351+ let balance = locked_wallet. balance ( ) ;
352+ let spendable_amount_sats = self
353+ . get_balances_inner ( balance, cur_anchor_reserve_sats)
354+ . map ( |( _, s) | s)
355+ . unwrap_or ( 0 ) ;
356+ let tx_fee_sats = locked_wallet
357+ . calculate_fee ( & psbt. unsigned_tx )
358+ . map_err ( |e| {
359+ log_error ! (
360+ self . logger,
361+ "Failed to calculate fee of candidate transaction: {}" ,
362+ e
363+ ) ;
364+ e
365+ } ) ?
366+ . to_sat ( ) ;
367+ if spendable_amount_sats < amount_sats. saturating_add ( tx_fee_sats) {
368+ log_error ! ( self . logger,
369+ "Unable to send payment due to insufficient funds. Available: {}sats, Required: {}sats + {}sats fee" ,
370+ spendable_amount_sats,
371+ amount_sats,
372+ tx_fee_sats,
373+ ) ;
374+ return Err ( Error :: InsufficientFunds ) ;
375+ }
376+ } ,
377+ OnchainSendAmount :: AllRetainingReserve { cur_anchor_reserve_sats } => {
378+ let balance = locked_wallet. balance ( ) ;
379+ let spendable_amount_sats = self
380+ . get_balances_inner ( balance, cur_anchor_reserve_sats)
381+ . map ( |( _, s) | s)
382+ . unwrap_or ( 0 ) ;
383+ let ( sent, received) = locked_wallet. sent_and_received ( & psbt. unsigned_tx ) ;
384+ let drain_amount = sent - received;
385+ if spendable_amount_sats < drain_amount. to_sat ( ) {
386+ log_error ! ( self . logger,
387+ "Unable to send payment due to insufficient funds. Available: {}sats, Required: {}" ,
388+ spendable_amount_sats,
389+ drain_amount,
390+ ) ;
391+ return Err ( Error :: InsufficientFunds ) ;
392+ }
393+ } ,
394+ _ => { } ,
395+ }
396+
270397 match locked_wallet. sign ( & mut psbt, SignOptions :: default ( ) ) {
271398 Ok ( finalized) => {
272399 if !finalized {
@@ -295,21 +422,33 @@ where
295422
296423 let txid = tx. compute_txid ( ) ;
297424
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- ) ;
425+ match send_amount {
426+ OnchainSendAmount :: ExactRetainingReserve { amount_sats, .. } => {
427+ log_info ! (
428+ self . logger,
429+ "Created new transaction {} sending {}sats on-chain to address {}" ,
430+ txid,
431+ amount_sats,
432+ address
433+ ) ;
434+ } ,
435+ OnchainSendAmount :: AllRetainingReserve { cur_anchor_reserve_sats } => {
436+ log_info ! (
437+ self . logger,
438+ "Created new transaction {} sending available on-chain funds retaining a reserve of {}sats to address {}" ,
439+ txid,
440+ cur_anchor_reserve_sats,
441+ address,
442+ ) ;
443+ } ,
444+ OnchainSendAmount :: AllDrainingReserve => {
445+ log_info ! (
446+ self . logger,
447+ "Created new transaction {} sending all available on-chain funds to address {}" ,
448+ txid,
449+ address
450+ ) ;
451+ } ,
313452 }
314453
315454 Ok ( txid)
0 commit comments