Skip to content

Commit 55e6bae

Browse files
committed
Don't fail send_all retaining reserves for 0 channels
Previouly, `OnchainPayment::send_all_to_address` would fail in the `retain_reserves` mode if the maintained reserves were below the dust limit. Most notably this would happen if we had no channels open at all. Here, we fix this by simply falling back to the draining case (not considering reserves) if the anchor reserves are below dust. We also add a unit test that would have caught this regression in the first place.
1 parent 5586b69 commit 55e6bae

File tree

2 files changed

+141
-49
lines changed

2 files changed

+141
-49
lines changed

src/wallet/mod.rs

Lines changed: 61 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -363,61 +363,73 @@ where
363363
tx_builder
364364
},
365365
OnchainSendAmount::AllRetainingReserve { cur_anchor_reserve_sats } => {
366-
let change_address_info = locked_wallet.peek_address(KeychainKind::Internal, 0);
367-
let balance = locked_wallet.balance();
368-
let spendable_amount_sats = self
369-
.get_balances_inner(balance, cur_anchor_reserve_sats)
370-
.map(|(_, s)| s)
371-
.unwrap_or(0);
372-
let tmp_tx = {
373-
let mut tmp_tx_builder = locked_wallet.build_tx();
374-
tmp_tx_builder
375-
.drain_wallet()
376-
.drain_to(address.script_pubkey())
377-
.add_recipient(
378-
change_address_info.address.script_pubkey(),
379-
Amount::from_sat(cur_anchor_reserve_sats),
380-
)
381-
.fee_rate(fee_rate);
382-
match tmp_tx_builder.finish() {
383-
Ok(psbt) => psbt.unsigned_tx,
384-
Err(err) => {
366+
const DUST_LIMIT_SATS: u64 = 546;
367+
if cur_anchor_reserve_sats > DUST_LIMIT_SATS {
368+
let change_address_info =
369+
locked_wallet.peek_address(KeychainKind::Internal, 0);
370+
let balance = locked_wallet.balance();
371+
let spendable_amount_sats = self
372+
.get_balances_inner(balance, cur_anchor_reserve_sats)
373+
.map(|(_, s)| s)
374+
.unwrap_or(0);
375+
let tmp_tx = {
376+
let mut tmp_tx_builder = locked_wallet.build_tx();
377+
tmp_tx_builder
378+
.drain_wallet()
379+
.drain_to(address.script_pubkey())
380+
.add_recipient(
381+
change_address_info.address.script_pubkey(),
382+
Amount::from_sat(cur_anchor_reserve_sats),
383+
)
384+
.fee_rate(fee_rate);
385+
match tmp_tx_builder.finish() {
386+
Ok(psbt) => psbt.unsigned_tx,
387+
Err(err) => {
388+
log_error!(
389+
self.logger,
390+
"Failed to create temporary transaction: {}",
391+
err
392+
);
393+
return Err(err.into());
394+
},
395+
}
396+
};
397+
398+
let estimated_tx_fee =
399+
locked_wallet.calculate_fee(&tmp_tx).map_err(|e| {
385400
log_error!(
386401
self.logger,
387-
"Failed to create temporary transaction: {}",
388-
err
402+
"Failed to calculate fee of temporary transaction: {}",
403+
e
389404
);
390-
return Err(err.into());
391-
},
392-
}
393-
};
394-
395-
let estimated_tx_fee = locked_wallet.calculate_fee(&tmp_tx).map_err(|e| {
396-
log_error!(
397-
self.logger,
398-
"Failed to calculate fee of temporary transaction: {}",
399-
e
405+
e
406+
})?;
407+
let estimated_spendable_amount = Amount::from_sat(
408+
spendable_amount_sats.saturating_sub(estimated_tx_fee.to_sat()),
400409
);
401-
e
402-
})?;
403-
let estimated_spendable_amount = Amount::from_sat(
404-
spendable_amount_sats.saturating_sub(estimated_tx_fee.to_sat()),
405-
);
406410

407-
if estimated_spendable_amount == Amount::ZERO {
408-
log_error!(self.logger,
409-
"Unable to send payment without infringing on Anchor reserves. Available: {}sats, estimated fee required: {}sats.",
410-
spendable_amount_sats,
411-
estimated_tx_fee,
412-
);
413-
return Err(Error::InsufficientFunds);
414-
}
411+
if estimated_spendable_amount == Amount::ZERO {
412+
log_error!(self.logger,
413+
"Unable to send payment without infringing on Anchor reserves. Available: {}sats, estimated fee required: {}sats.",
414+
spendable_amount_sats,
415+
estimated_tx_fee,
416+
);
417+
return Err(Error::InsufficientFunds);
418+
}
415419

416-
let mut tx_builder = locked_wallet.build_tx();
417-
tx_builder
418-
.add_recipient(address.script_pubkey(), estimated_spendable_amount)
419-
.fee_absolute(estimated_tx_fee);
420-
tx_builder
420+
let mut tx_builder = locked_wallet.build_tx();
421+
tx_builder
422+
.add_recipient(address.script_pubkey(), estimated_spendable_amount)
423+
.fee_absolute(estimated_tx_fee);
424+
tx_builder
425+
} else {
426+
let mut tx_builder = locked_wallet.build_tx();
427+
tx_builder
428+
.drain_wallet()
429+
.drain_to(address.script_pubkey())
430+
.fee_rate(fee_rate);
431+
tx_builder
432+
}
421433
},
422434
OnchainSendAmount::AllDrainingReserve => {
423435
let mut tx_builder = locked_wallet.build_tx();

tests/integration_tests_rust.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,86 @@ fn onchain_send_receive() {
496496
assert_eq!(node_b_payments.len(), 5);
497497
}
498498

499+
#[test]
500+
fn onchain_send_all_retains_reserve() {
501+
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
502+
let chain_source = TestChainSource::Esplora(&electrsd);
503+
let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false);
504+
505+
// Setup nodes
506+
let addr_a = node_a.onchain_payment().new_address().unwrap();
507+
let addr_b = node_b.onchain_payment().new_address().unwrap();
508+
509+
let premine_amount_sat = 1_000_000;
510+
let reserve_amount_sat = 25_000;
511+
let onchain_fee_buffer_sat = 1000;
512+
premine_and_distribute_funds(
513+
&bitcoind.client,
514+
&electrsd.client,
515+
vec![addr_a.clone(), addr_b.clone()],
516+
Amount::from_sat(premine_amount_sat),
517+
);
518+
519+
node_a.sync_wallets().unwrap();
520+
node_b.sync_wallets().unwrap();
521+
assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, premine_amount_sat);
522+
assert_eq!(node_b.list_balances().spendable_onchain_balance_sats, premine_amount_sat);
523+
524+
// Send all over, with 0 reserve as we don't have any channels open.
525+
let txid = node_a.onchain_payment().send_all_to_address(&addr_b, true, None).unwrap();
526+
527+
wait_for_tx(&electrsd.client, txid);
528+
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6);
529+
530+
node_a.sync_wallets().unwrap();
531+
node_b.sync_wallets().unwrap();
532+
assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, 0);
533+
assert!(((premine_amount_sat * 2 - onchain_fee_buffer_sat)..(premine_amount_sat * 2))
534+
.contains(&node_b.list_balances().spendable_onchain_balance_sats));
535+
536+
// Refill to make sure we have enough reserve for the channel open.
537+
let txid = bitcoind
538+
.client
539+
.send_to_address(&addr_a, Amount::from_sat(reserve_amount_sat))
540+
.unwrap()
541+
.0
542+
.parse()
543+
.unwrap();
544+
wait_for_tx(&electrsd.client, txid);
545+
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6);
546+
node_a.sync_wallets().unwrap();
547+
node_b.sync_wallets().unwrap();
548+
assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, reserve_amount_sat);
549+
550+
// Open a channel.
551+
open_channel(&node_b, &node_a, premine_amount_sat, false, &electrsd);
552+
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6);
553+
node_a.sync_wallets().unwrap();
554+
node_b.sync_wallets().unwrap();
555+
expect_channel_ready_event!(node_a, node_b.node_id());
556+
expect_channel_ready_event!(node_b, node_a.node_id());
557+
558+
assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, 0);
559+
assert!(((premine_amount_sat - reserve_amount_sat - onchain_fee_buffer_sat)
560+
..premine_amount_sat)
561+
.contains(&node_b.list_balances().spendable_onchain_balance_sats));
562+
563+
// Send all over again, this time ensuring the reserve is accounted for
564+
let txid = node_b.onchain_payment().send_all_to_address(&addr_a, true, None).unwrap();
565+
566+
wait_for_tx(&electrsd.client, txid);
567+
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6);
568+
569+
node_a.sync_wallets().unwrap();
570+
node_b.sync_wallets().unwrap();
571+
572+
assert_eq!(node_b.list_balances().total_onchain_balance_sats, reserve_amount_sat);
573+
assert_eq!(node_b.list_balances().spendable_onchain_balance_sats, 0);
574+
assert!(((premine_amount_sat - reserve_amount_sat - onchain_fee_buffer_sat)
575+
..premine_amount_sat)
576+
.contains(&node_a.list_balances().spendable_onchain_balance_sats));
577+
}
578+
499579
#[test]
500580
fn onchain_wallet_recovery() {
501581
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();

0 commit comments

Comments
 (0)