Skip to content

Commit 2a6e971

Browse files
committed
Retain anchor reserves when sending all funds
Co-authored-by: Mat Balez <60949391+matbalez@users.noreply.github.com> Signed-off-by: Mat Balez <60949391+matbalez@users.noreply.github.com>
1 parent 0e4434d commit 2a6e971

6 files changed

Lines changed: 48 additions & 13 deletions

File tree

e2e-tests/tests/e2e.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ use ldk_node::lightning::offers::offer::Offer;
2323
use ldk_node::lightning_invoice::Bolt11Invoice;
2424
use ldk_server_client::client::EventStream;
2525
use ldk_server_client::ldk_server_grpc::api::{
26-
Bolt11ReceiveRequest, Bolt12ReceiveRequest, OnchainReceiveRequest, OpenChannelRequest,
26+
Bolt11ReceiveRequest, Bolt12ReceiveRequest, GetBalancesRequest, OnchainReceiveRequest,
27+
OpenChannelRequest,
2728
};
2829
use ldk_server_client::ldk_server_grpc::events::event_envelope::Event;
2930
use ldk_server_client::ldk_server_grpc::events::{
@@ -360,6 +361,45 @@ async fn test_cli_onchain_send() {
360361
assert!(!output["txid"].as_str().unwrap().is_empty());
361362
}
362363

364+
#[tokio::test]
365+
async fn test_cli_onchain_send_all_retains_anchor_reserve() {
366+
let bitcoind = TestBitcoind::new();
367+
let server_a = LdkServerHandle::start(&bitcoind).await;
368+
let server_b = LdkServerHandle::start(&bitcoind).await;
369+
370+
setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await;
371+
372+
let balances_before = server_a.client().get_balances(GetBalancesRequest {}).await.unwrap();
373+
let reserve_sats = balances_before.total_anchor_channels_reserve_sats;
374+
assert!(reserve_sats > 0);
375+
376+
let address =
377+
server_b.client().onchain_receive(OnchainReceiveRequest {}).await.unwrap().address;
378+
let output = run_cli(&server_a, &["onchain-send", &address, "--send-all", "true"]);
379+
assert!(!output["txid"].as_str().unwrap().is_empty());
380+
381+
mine_and_sync(&bitcoind, &[&server_a, &server_b], 6).await;
382+
383+
let timeout = Duration::from_secs(30);
384+
let start = std::time::Instant::now();
385+
loop {
386+
let balances = server_a.client().get_balances(GetBalancesRequest {}).await.unwrap();
387+
if balances.spendable_onchain_balance_sats == 0
388+
&& balances.total_onchain_balance_sats == reserve_sats
389+
{
390+
assert_eq!(balances.total_anchor_channels_reserve_sats, reserve_sats);
391+
break;
392+
}
393+
if start.elapsed() > timeout {
394+
panic!(
395+
"Timed out waiting for send-all to retain the {} sat Anchor reserve. Current balances: {:?}",
396+
reserve_sats, balances
397+
);
398+
}
399+
tokio::time::sleep(Duration::from_millis(500)).await;
400+
}
401+
}
402+
363403
#[tokio::test]
364404
async fn test_cli_connect_peer() {
365405
let bitcoind = TestBitcoind::new();

ldk-server-cli/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ enum Commands {
118118
amount: Option<Amount>,
119119
#[arg(
120120
long,
121-
help = "Send full balance to the address. Warning: will not retain on-chain reserves for anchor channels"
121+
help = "Send all available balance to the address while retaining on-chain reserves for anchor channels"
122122
)]
123123
send_all: Option<bool>,
124124
#[arg(

ldk-server-grpc/src/api.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,9 @@ pub struct OnchainSendRequest {
125125
#[prost(uint64, optional, tag = "2")]
126126
pub amount_sats: ::core::option::Option<u64>,
127127
/// If set, the amount_sats field should be unset.
128-
/// It indicates that node will send full balance to the specified address.
128+
/// It indicates that the node will send all available balance to the specified address.
129129
///
130-
/// Please note that when send_all is used this operation will **not** retain any on-chain reserves,
131-
/// which might be potentially dangerous if you have open Anchor channels for which you can't trust
132-
/// the counterparty to spend the Anchor output after channel closure.
130+
/// Any on-chain reserves needed for Anchor channels will be retained.
133131
/// See more: <https://docs.rs/ldk-node/latest/ldk_node/payment/struct.OnchainPayment.html#method.send_all_to_address>
134132
#[prost(bool, optional, tag = "3")]
135133
pub send_all: ::core::option::Option<bool>,

ldk-server-grpc/src/proto/api.proto

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,9 @@ message OnchainSendRequest {
103103
optional uint64 amount_sats = 2;
104104

105105
// If set, the amount_sats field should be unset.
106-
// It indicates that node will send full balance to the specified address.
106+
// It indicates that the node will send all available balance to the specified address.
107107
//
108-
// Please note that when send_all is used this operation will **not** retain any on-chain reserves,
109-
// which might be potentially dangerous if you have open Anchor channels for which you can't trust
110-
// the counterparty to spend the Anchor output after channel closure.
108+
// Any on-chain reserves needed for Anchor channels will be retained.
111109
// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.OnchainPayment.html#method.send_all_to_address
112110
optional bool send_all = 3;
113111

ldk-server-mcp/src/tools/schema.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ pub fn onchain_send_schema() -> Value {
168168
},
169169
"send_all": {
170170
"type": "boolean",
171-
"description": "If true, send full balance (ignores amount_sats). Warning: will not retain on-chain reserves for anchor channels"
171+
"description": "If true, send all available balance while retaining on-chain reserves for anchor channels (amount_sats must be unset)"
172172
},
173173
"fee_rate_sat_per_vb": {
174174
"type": "integer",

ldk-server/src/api/onchain_send.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,8 @@ pub(crate) async fn handle_onchain_send_request(
3535
(Some(amount_sats), None) => {
3636
context.node.onchain_payment().send_to_address(&address, amount_sats, fee_rate)?
3737
},
38-
// Retain existing api behaviour to not retain reserves on `send_all_to_address`.
3938
(None, Some(true)) => {
40-
context.node.onchain_payment().send_all_to_address(&address, false, fee_rate)?
39+
context.node.onchain_payment().send_all_to_address(&address, true, fee_rate)?
4140
},
4241
_ => {
4342
return Err(LdkServerError::new(

0 commit comments

Comments
 (0)