Skip to content

Commit d6672b6

Browse files
committed
Allow to override fee rates for onchain payments
We allow to override our fee estimator in the `send_to_address` and `send_all_to_address` API methods. To this end, we implement a bindings-compatible wrapper around `bitcoin::FeeRate`.
1 parent 03c4ab0 commit d6672b6

File tree

6 files changed

+59
-15
lines changed

6 files changed

+59
-15
lines changed

bindings/ldk_node.udl

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,19 @@ interface OnchainPayment {
161161
[Throws=NodeError]
162162
Address new_address();
163163
[Throws=NodeError]
164-
Txid send_to_address([ByRef]Address address, u64 amount_sats);
164+
Txid send_to_address([ByRef]Address address, u64 amount_sats, FeeRate? fee_rate);
165165
[Throws=NodeError]
166-
Txid send_all_to_address([ByRef]Address address, boolean retain_reserve);
166+
Txid send_all_to_address([ByRef]Address address, boolean retain_reserve, FeeRate? fee_rate);
167+
};
168+
169+
interface FeeRate {
170+
[Name=from_sat_per_kwu]
171+
constructor(u64 sat_kwu);
172+
[Name=from_sat_per_vb_unchecked]
173+
constructor(u64 sat_vb);
174+
u64 to_sat_per_kwu();
175+
u64 to_sat_per_vb_floor();
176+
u64 to_sat_per_vb_ceil();
167177
};
168178

169179
interface UnifiedQrPayment {

src/payment/onchain.rs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,24 @@ use bitcoin::{Address, Txid};
1717

1818
use std::sync::{Arc, RwLock};
1919

20+
#[cfg(not(feature = "uniffi"))]
21+
type FeeRate = bitcoin::FeeRate;
22+
#[cfg(feature = "uniffi")]
23+
type FeeRate = Arc<bitcoin::FeeRate>;
24+
25+
macro_rules! maybe_map_fee_rate_opt {
26+
($fee_rate_opt: expr) => {{
27+
#[cfg(not(feature = "uniffi"))]
28+
{
29+
$fee_rate_opt
30+
}
31+
#[cfg(feature = "uniffi")]
32+
{
33+
$fee_rate_opt.map(|f| *f)
34+
}
35+
}};
36+
}
37+
2038
/// A payment handler allowing to send and receive on-chain payments.
2139
///
2240
/// Should be retrieved by calling [`Node::onchain_payment`].
@@ -50,9 +68,12 @@ impl OnchainPayment {
5068
/// This will respect any on-chain reserve we need to keep, i.e., won't allow to cut into
5169
/// [`BalanceDetails::total_anchor_channels_reserve_sats`].
5270
///
71+
/// If `fee_rate` is set it will be used on the resulting transaction. Otherwise we'll retrieve
72+
/// a reasonable estimate from the configured chain source.
73+
///
5374
/// [`BalanceDetails::total_anchor_channels_reserve_sats`]: crate::BalanceDetails::total_anchor_channels_reserve_sats
5475
pub fn send_to_address(
55-
&self, address: &bitcoin::Address, amount_sats: u64,
76+
&self, address: &bitcoin::Address, amount_sats: u64, fee_rate: Option<FeeRate>,
5677
) -> Result<Txid, Error> {
5778
let rt_lock = self.runtime.read().unwrap();
5879
if rt_lock.is_none() {
@@ -63,7 +84,8 @@ impl OnchainPayment {
6384
crate::total_anchor_channels_reserve_sats(&self.channel_manager, &self.config);
6485
let send_amount =
6586
OnchainSendAmount::ExactRetainingReserve { amount_sats, cur_anchor_reserve_sats };
66-
self.wallet.send_to_address(address, send_amount)
87+
let fee_rate_opt = maybe_map_fee_rate_opt!(fee_rate);
88+
self.wallet.send_to_address(address, send_amount, fee_rate_opt)
6789
}
6890

6991
/// Send an on-chain payment to the given address, draining the available funds.
@@ -77,9 +99,12 @@ impl OnchainPayment {
7799
/// will try to send all spendable onchain funds, i.e.,
78100
/// [`BalanceDetails::spendable_onchain_balance_sats`].
79101
///
102+
/// If `fee_rate` is set it will be used on the resulting transaction. Otherwise a reasonable
103+
/// we'll retrieve an estimate from the configured chain source.
104+
///
80105
/// [`BalanceDetails::spendable_onchain_balance_sats`]: crate::balance::BalanceDetails::spendable_onchain_balance_sats
81106
pub fn send_all_to_address(
82-
&self, address: &bitcoin::Address, retain_reserves: bool,
107+
&self, address: &bitcoin::Address, retain_reserves: bool, fee_rate: Option<FeeRate>,
83108
) -> Result<Txid, Error> {
84109
let rt_lock = self.runtime.read().unwrap();
85110
if rt_lock.is_none() {
@@ -94,6 +119,7 @@ impl OnchainPayment {
94119
OnchainSendAmount::AllDrainingReserve
95120
};
96121

97-
self.wallet.send_to_address(address, send_amount)
122+
let fee_rate_opt = maybe_map_fee_rate_opt!(fee_rate);
123+
self.wallet.send_to_address(address, send_amount, fee_rate_opt)
98124
}
99125
}

src/payment/unified_qr.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,11 @@ impl UnifiedQrPayment {
156156
},
157157
};
158158

159-
let txid =
160-
self.onchain_payment.send_to_address(&uri_network_checked.address, amount.to_sat())?;
159+
let txid = self.onchain_payment.send_to_address(
160+
&uri_network_checked.address,
161+
amount.to_sat(),
162+
None,
163+
)?;
161164

162165
Ok(QrPaymentResult::Onchain { txid })
163166
}

src/uniffi_types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
3030

3131
pub use lightning_invoice::Bolt11Invoice;
3232

33-
pub use bitcoin::{Address, BlockHash, Network, OutPoint, Txid};
33+
pub use bitcoin::{Address, BlockHash, FeeRate, Network, OutPoint, Txid};
3434

3535
pub use bip39::Mnemonic;
3636

src/wallet/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ use bitcoin::secp256k1::ecdh::SharedSecret;
3939
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
4040
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, Signing};
4141
use bitcoin::{
42-
Amount, ScriptBuf, Transaction, TxOut, Txid, WPubkeyHash, WitnessProgram, WitnessVersion,
42+
Amount, FeeRate, ScriptBuf, Transaction, TxOut, Txid, WPubkeyHash, WitnessProgram,
43+
WitnessVersion,
4344
};
4445

4546
use std::ops::Deref;
@@ -239,9 +240,12 @@ where
239240

240241
pub(crate) fn send_to_address(
241242
&self, address: &bitcoin::Address, send_amount: OnchainSendAmount,
243+
fee_rate: Option<FeeRate>,
242244
) -> Result<Txid, Error> {
245+
// Use the set fee_rate or default to fee estimation.
243246
let confirmation_target = ConfirmationTarget::OnchainPayment;
244-
let fee_rate = self.fee_estimator.estimate_fee_rate(confirmation_target);
247+
let fee_rate =
248+
fee_rate.unwrap_or_else(|| self.fee_estimator.estimate_fee_rate(confirmation_target));
245249

246250
let tx = {
247251
let mut locked_wallet = self.inner.lock().unwrap();

tests/integration_tests_rust.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -315,11 +315,12 @@ fn onchain_spend_receive() {
315315

316316
assert_eq!(
317317
Err(NodeError::InsufficientFunds),
318-
node_a.onchain_payment().send_to_address(&addr_b, expected_node_a_balance + 1)
318+
node_a.onchain_payment().send_to_address(&addr_b, expected_node_a_balance + 1, None)
319319
);
320320

321321
let amount_to_send_sats = 1000;
322-
let txid = node_b.onchain_payment().send_to_address(&addr_a, amount_to_send_sats).unwrap();
322+
let txid =
323+
node_b.onchain_payment().send_to_address(&addr_a, amount_to_send_sats, None).unwrap();
323324
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6);
324325
wait_for_tx(&electrsd.client, txid);
325326

@@ -334,7 +335,7 @@ fn onchain_spend_receive() {
334335
assert!(node_b.list_balances().spendable_onchain_balance_sats < expected_node_b_balance_upper);
335336

336337
let addr_b = node_b.onchain_payment().new_address().unwrap();
337-
let txid = node_a.onchain_payment().send_all_to_address(&addr_b, true).unwrap();
338+
let txid = node_a.onchain_payment().send_all_to_address(&addr_b, true, None).unwrap();
338339
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6);
339340
wait_for_tx(&electrsd.client, txid);
340341

@@ -350,7 +351,7 @@ fn onchain_spend_receive() {
350351
assert!(node_b.list_balances().spendable_onchain_balance_sats < expected_node_b_balance_upper);
351352

352353
let addr_b = node_b.onchain_payment().new_address().unwrap();
353-
let txid = node_a.onchain_payment().send_all_to_address(&addr_b, false).unwrap();
354+
let txid = node_a.onchain_payment().send_all_to_address(&addr_b, false, None).unwrap();
354355
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6);
355356
wait_for_tx(&electrsd.client, txid);
356357

0 commit comments

Comments
 (0)