Skip to content

Commit 2165ca5

Browse files
committed
Wire destination-aware max_sendable via find_route
The reported fee budget is built from route.get_total_fees(), which is what LDK itself would charge for a send of that size. Slightly conservative: total_fees is computed from the full balance so with the sent amount they will be less. This provides a buffer and could be tweaked by iterating the estimate. On top of total_fees the budget applies a multiplier. The chosen route can fail at send time and LDK retries along a more expensive path. A multiplier scales with the original route's cost so retries can actually reach those alternatives. Exposed as route_retry_fee_multiplier_bps so operators can trade reported max sendable for payment success rate. Starting low to minimise headroom left on the table; bump up if production retries surface "insufficient fee budget" failures too often. FixedAmount returns Err(FixedAmount { amount_msat }) because there's nothing to estimate when the payee dictates the amount; the variant carries the amount so callers don't re-extract it. BOLT12 offers, LNURL-pay, and HRN destinations fall back to the simple buffer for now. BOLT12 needs the invoice (not the offer) for from_bolt12_invoice, which depends on the invoice fetch deferred upstream. LNURL-pay and HRN need to resolve into a concrete invoice first; that resolution will move into this module shortly. BOLT11 round-trips through its bech32 string: bitcoin-payment- instructions pins upstream rust-lightning's Bolt11Invoice while ldk-node pulls the moneydevkit fork. Wire-compatible, distinct types. A re-parse failure falls back to Buffer rather than panicking.
1 parent 69b3d9d commit 2165ca5

3 files changed

Lines changed: 323 additions & 109 deletions

File tree

src/daemon/config.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ struct SpliceSection {
9696
struct MaxSendableSection {
9797
fee_buffer_bps: Option<u16>,
9898
fee_buffer_floor_sats: Option<u64>,
99+
route_retry_fee_multiplier_bps: Option<u16>,
99100
}
100101

101102
pub struct MdkConfig {
@@ -190,6 +191,9 @@ pub fn load_config(path: &str) -> io::Result<MdkConfig> {
190191
fee_buffer_floor_sats: s
191192
.fee_buffer_floor_sats
192193
.unwrap_or(defaults.fee_buffer_floor_sats),
194+
route_retry_fee_multiplier_bps: s
195+
.route_retry_fee_multiplier_bps
196+
.unwrap_or(defaults.route_retry_fee_multiplier_bps),
193197
}
194198
}
195199
None => MaxSendableConfig::default(),

src/mdk/client.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use tokio_util::sync::CancellationToken;
1717

1818
use crate::mdk::error::{MdkError, SpliceError};
1919
use crate::mdk::max_sendable::{
20-
compute_estimate, ChannelSnapshot, MaxSendableConfig, MaxSendableError, MaxSendableEstimate,
20+
self, ChannelSnapshot, MaxSendableConfig, MaxSendableError, MaxSendableEstimate,
2121
};
2222
use crate::mdk::mdk_api::client::MdkApiClient;
2323
use crate::mdk::mdk_api::types::{
@@ -146,27 +146,29 @@ impl MdkClient {
146146

147147
/// Best-effort estimate of the largest amount that can flow out
148148
/// over Lightning right now, with routing-fee headroom subtracted.
149-
/// Computed inline from `node.list_channels()` on every call so
150-
/// the result reflects in-flight HTLCs and reserve as of *now*.
149+
/// Recomputed from `node.list_channels()` on every call so the
150+
/// result reflects in-flight HTLCs and reserve as of *now*.
151151
///
152-
/// `dest` is currently ignored — only the `None` (destination-agnostic
153-
/// buffer) path is implemented.
152+
/// `dest = None` returns a buffer-based estimate; `Some(_)`
153+
/// drives `Node::find_route` and subtracts the real fees. See
154+
/// [`crate::mdk::max_sendable`] for the full dispatch table.
154155
pub fn max_sendable(
155156
&self,
156157
dest: Option<&PaymentInstructions>,
157158
) -> Result<MaxSendableEstimate, MaxSendableError> {
158-
debug_assert!(
159-
dest.is_none(),
160-
"destination-aware max_sendable is not yet implemented"
161-
);
162-
let _ = dest;
163159
let snaps: Vec<ChannelSnapshot> = self
164160
.node
165161
.list_channels()
166162
.iter()
167163
.map(ChannelSnapshot::from)
168164
.collect();
169-
compute_estimate(&snaps, &self.lsp_pubkey, &self.max_sendable_cfg)
165+
max_sendable::compute_estimate(
166+
dest,
167+
&snaps,
168+
&self.lsp_pubkey,
169+
&self.max_sendable_cfg,
170+
|rp| self.node.find_route(rp).map_err(|e| format!("{e}")),
171+
)
170172
}
171173

172174
/// Splice `amount_sats` of confirmed on-chain funds into the

0 commit comments

Comments
 (0)