Skip to content

Commit 80b7c92

Browse files
authored
Merge pull request #434 from tnull/2025-01-allow-to-override-feerate
Allow to override fee rates for onchain payments
2 parents 3cd1e98 + d6672b6 commit 80b7c92

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
@@ -167,9 +167,19 @@ interface OnchainPayment {
167167
[Throws=NodeError]
168168
Address new_address();
169169
[Throws=NodeError]
170-
Txid send_to_address([ByRef]Address address, u64 amount_sats);
170+
Txid send_to_address([ByRef]Address address, u64 amount_sats, FeeRate? fee_rate);
171171
[Throws=NodeError]
172-
Txid send_all_to_address([ByRef]Address address, boolean retain_reserve);
172+
Txid send_all_to_address([ByRef]Address address, boolean retain_reserve, FeeRate? fee_rate);
173+
};
174+
175+
interface FeeRate {
176+
[Name=from_sat_per_kwu]
177+
constructor(u64 sat_kwu);
178+
[Name=from_sat_per_vb_unchecked]
179+
constructor(u64 sat_vb);
180+
u64 to_sat_per_kwu();
181+
u64 to_sat_per_vb_floor();
182+
u64 to_sat_per_vb_ceil();
173183
};
174184

175185
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
@@ -163,8 +163,11 @@ impl UnifiedQrPayment {
163163
},
164164
};
165165

166-
let txid =
167-
self.onchain_payment.send_to_address(&uri_network_checked.address, amount.to_sat())?;
166+
let txid = self.onchain_payment.send_to_address(
167+
&uri_network_checked.address,
168+
amount.to_sat(),
169+
None,
170+
)?;
168171

169172
Ok(QrPaymentResult::Onchain { txid })
170173
}

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, Description};
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
@@ -321,11 +321,12 @@ fn onchain_spend_receive() {
321321

322322
assert_eq!(
323323
Err(NodeError::InsufficientFunds),
324-
node_a.onchain_payment().send_to_address(&addr_b, expected_node_a_balance + 1)
324+
node_a.onchain_payment().send_to_address(&addr_b, expected_node_a_balance + 1, None)
325325
);
326326

327327
let amount_to_send_sats = 1000;
328-
let txid = node_b.onchain_payment().send_to_address(&addr_a, amount_to_send_sats).unwrap();
328+
let txid =
329+
node_b.onchain_payment().send_to_address(&addr_a, amount_to_send_sats, None).unwrap();
329330
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6);
330331
wait_for_tx(&electrsd.client, txid);
331332

@@ -340,7 +341,7 @@ fn onchain_spend_receive() {
340341
assert!(node_b.list_balances().spendable_onchain_balance_sats < expected_node_b_balance_upper);
341342

342343
let addr_b = node_b.onchain_payment().new_address().unwrap();
343-
let txid = node_a.onchain_payment().send_all_to_address(&addr_b, true).unwrap();
344+
let txid = node_a.onchain_payment().send_all_to_address(&addr_b, true, None).unwrap();
344345
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6);
345346
wait_for_tx(&electrsd.client, txid);
346347

@@ -356,7 +357,7 @@ fn onchain_spend_receive() {
356357
assert!(node_b.list_balances().spendable_onchain_balance_sats < expected_node_b_balance_upper);
357358

358359
let addr_b = node_b.onchain_payment().new_address().unwrap();
359-
let txid = node_a.onchain_payment().send_all_to_address(&addr_b, false).unwrap();
360+
let txid = node_a.onchain_payment().send_all_to_address(&addr_b, false, None).unwrap();
360361
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6);
361362
wait_for_tx(&electrsd.client, txid);
362363

0 commit comments

Comments
 (0)