Skip to content

Commit

Permalink
Merge pull request #434 from tnull/2025-01-allow-to-override-feerate
Browse files Browse the repository at this point in the history
Allow to override fee rates for onchain payments
  • Loading branch information
tnull authored Jan 27, 2025
2 parents 3cd1e98 + d6672b6 commit 80b7c92
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 15 deletions.
14 changes: 12 additions & 2 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,19 @@ interface OnchainPayment {
[Throws=NodeError]
Address new_address();
[Throws=NodeError]
Txid send_to_address([ByRef]Address address, u64 amount_sats);
Txid send_to_address([ByRef]Address address, u64 amount_sats, FeeRate? fee_rate);
[Throws=NodeError]
Txid send_all_to_address([ByRef]Address address, boolean retain_reserve);
Txid send_all_to_address([ByRef]Address address, boolean retain_reserve, FeeRate? fee_rate);
};

interface FeeRate {
[Name=from_sat_per_kwu]
constructor(u64 sat_kwu);
[Name=from_sat_per_vb_unchecked]
constructor(u64 sat_vb);
u64 to_sat_per_kwu();
u64 to_sat_per_vb_floor();
u64 to_sat_per_vb_ceil();
};

interface UnifiedQrPayment {
Expand Down
34 changes: 30 additions & 4 deletions src/payment/onchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@ use bitcoin::{Address, Txid};

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

#[cfg(not(feature = "uniffi"))]
type FeeRate = bitcoin::FeeRate;
#[cfg(feature = "uniffi")]
type FeeRate = Arc<bitcoin::FeeRate>;

macro_rules! maybe_map_fee_rate_opt {
($fee_rate_opt: expr) => {{
#[cfg(not(feature = "uniffi"))]
{
$fee_rate_opt
}
#[cfg(feature = "uniffi")]
{
$fee_rate_opt.map(|f| *f)
}
}};
}

/// A payment handler allowing to send and receive on-chain payments.
///
/// Should be retrieved by calling [`Node::onchain_payment`].
Expand Down Expand Up @@ -50,9 +68,12 @@ impl OnchainPayment {
/// This will respect any on-chain reserve we need to keep, i.e., won't allow to cut into
/// [`BalanceDetails::total_anchor_channels_reserve_sats`].
///
/// If `fee_rate` is set it will be used on the resulting transaction. Otherwise we'll retrieve
/// a reasonable estimate from the configured chain source.
///
/// [`BalanceDetails::total_anchor_channels_reserve_sats`]: crate::BalanceDetails::total_anchor_channels_reserve_sats
pub fn send_to_address(
&self, address: &bitcoin::Address, amount_sats: u64,
&self, address: &bitcoin::Address, amount_sats: u64, fee_rate: Option<FeeRate>,
) -> Result<Txid, Error> {
let rt_lock = self.runtime.read().unwrap();
if rt_lock.is_none() {
Expand All @@ -63,7 +84,8 @@ impl OnchainPayment {
crate::total_anchor_channels_reserve_sats(&self.channel_manager, &self.config);
let send_amount =
OnchainSendAmount::ExactRetainingReserve { amount_sats, cur_anchor_reserve_sats };
self.wallet.send_to_address(address, send_amount)
let fee_rate_opt = maybe_map_fee_rate_opt!(fee_rate);
self.wallet.send_to_address(address, send_amount, fee_rate_opt)
}

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

self.wallet.send_to_address(address, send_amount)
let fee_rate_opt = maybe_map_fee_rate_opt!(fee_rate);
self.wallet.send_to_address(address, send_amount, fee_rate_opt)
}
}
7 changes: 5 additions & 2 deletions src/payment/unified_qr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,11 @@ impl UnifiedQrPayment {
},
};

let txid =
self.onchain_payment.send_to_address(&uri_network_checked.address, amount.to_sat())?;
let txid = self.onchain_payment.send_to_address(
&uri_network_checked.address,
amount.to_sat(),
None,
)?;

Ok(QrPaymentResult::Onchain { txid })
}
Expand Down
2 changes: 1 addition & 1 deletion src/uniffi_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};

pub use lightning_invoice::{Bolt11Invoice, Description};

pub use bitcoin::{Address, BlockHash, Network, OutPoint, Txid};
pub use bitcoin::{Address, BlockHash, FeeRate, Network, OutPoint, Txid};

pub use bip39::Mnemonic;

Expand Down
8 changes: 6 additions & 2 deletions src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, Signing};
use bitcoin::{
Amount, ScriptBuf, Transaction, TxOut, Txid, WPubkeyHash, WitnessProgram, WitnessVersion,
Amount, FeeRate, ScriptBuf, Transaction, TxOut, Txid, WPubkeyHash, WitnessProgram,
WitnessVersion,
};

use std::ops::Deref;
Expand Down Expand Up @@ -239,9 +240,12 @@ where

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

let tx = {
let mut locked_wallet = self.inner.lock().unwrap();
Expand Down
9 changes: 5 additions & 4 deletions tests/integration_tests_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,11 +321,12 @@ fn onchain_spend_receive() {

assert_eq!(
Err(NodeError::InsufficientFunds),
node_a.onchain_payment().send_to_address(&addr_b, expected_node_a_balance + 1)
node_a.onchain_payment().send_to_address(&addr_b, expected_node_a_balance + 1, None)
);

let amount_to_send_sats = 1000;
let txid = node_b.onchain_payment().send_to_address(&addr_a, amount_to_send_sats).unwrap();
let txid =
node_b.onchain_payment().send_to_address(&addr_a, amount_to_send_sats, None).unwrap();
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6);
wait_for_tx(&electrsd.client, txid);

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

let addr_b = node_b.onchain_payment().new_address().unwrap();
let txid = node_a.onchain_payment().send_all_to_address(&addr_b, true).unwrap();
let txid = node_a.onchain_payment().send_all_to_address(&addr_b, true, None).unwrap();
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6);
wait_for_tx(&electrsd.client, txid);

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

let addr_b = node_b.onchain_payment().new_address().unwrap();
let txid = node_a.onchain_payment().send_all_to_address(&addr_b, false).unwrap();
let txid = node_a.onchain_payment().send_all_to_address(&addr_b, false, None).unwrap();
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6);
wait_for_tx(&electrsd.client, txid);

Expand Down

0 comments on commit 80b7c92

Please sign in to comment.