Skip to content

Commit 8257cc3

Browse files
authored
Merge pull request #3457 from arik-so/min_relay_fee_fix
Fix min relay fee to be 1s/vB
2 parents bd85a29 + 8fd2dee commit 8257cc3

File tree

4 files changed

+152
-48
lines changed

4 files changed

+152
-48
lines changed

lightning/src/chain/chaininterface.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ pub trait FeeEstimator {
176176
}
177177

178178
/// Minimum relay fee as required by bitcoin network mempool policy.
179-
pub const MIN_RELAY_FEE_SAT_PER_1000_WEIGHT: u64 = 4000;
179+
pub const INCREMENTAL_RELAY_FEE_SAT_PER_1000_WEIGHT: u64 = 253;
180180
/// Minimum feerate that takes a sane approach to bitcoind weight-to-vbytes rounding.
181181
/// See the following Core Lightning commit for an explanation:
182182
/// <https://github.com/ElementsProject/lightning/commit/2e687b9b352c9092b5e8bd4a688916ac50b44af0>

lightning/src/chain/onchaintx.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ pub(crate) enum OnchainClaim {
215215
}
216216

217217
/// Represents the different feerate strategies a pending request can use when generating a claim.
218+
#[derive(Debug)]
218219
pub(crate) enum FeerateStrategy {
219220
/// We must reuse the most recently used feerate, if any.
220221
RetryPrevious,

lightning/src/chain/package.rs

Lines changed: 114 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint};
3131
use crate::ln::channelmanager::MIN_CLTV_EXPIRY_DELTA;
3232
use crate::ln::msgs::DecodeError;
3333
use crate::chain::channelmonitor::COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
34-
use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT, compute_feerate_sat_per_1000_weight, FEERATE_FLOOR_SATS_PER_KW};
34+
use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, INCREMENTAL_RELAY_FEE_SAT_PER_1000_WEIGHT, compute_feerate_sat_per_1000_weight, FEERATE_FLOOR_SATS_PER_KW};
3535
use crate::chain::transaction::MaybeSignedTransaction;
3636
use crate::sign::ecdsa::EcdsaChannelSigner;
3737
use crate::chain::onchaintx::{FeerateStrategy, ExternalHTLCClaim, OnchainTxHandler};
@@ -1117,10 +1117,10 @@ impl PackageTemplate {
11171117
// If old feerate is 0, first iteration of this claim, use normal fee calculation
11181118
if self.feerate_previous != 0 {
11191119
if let Some((new_fee, feerate)) = feerate_bump(
1120-
predicted_weight, input_amounts, self.feerate_previous, feerate_strategy,
1121-
conf_target, fee_estimator, logger,
1120+
predicted_weight, input_amounts, dust_limit_sats, self.feerate_previous,
1121+
feerate_strategy, conf_target, fee_estimator, logger,
11221122
) {
1123-
return Some((cmp::max(input_amounts as i64 - new_fee as i64, dust_limit_sats as i64) as u64, feerate));
1123+
return Some((input_amounts.saturating_sub(new_fee), feerate));
11241124
}
11251125
} else {
11261126
if let Some((new_fee, feerate)) = compute_fee_from_spent_amounts(input_amounts, predicted_weight, conf_target, fee_estimator, logger) {
@@ -1243,7 +1243,7 @@ impl Readable for PackageTemplate {
12431243
/// fee and the corresponding updated feerate. If fee is under [`FEERATE_FLOOR_SATS_PER_KW`], we
12441244
/// return nothing.
12451245
///
1246-
/// [`FEERATE_FLOOR_SATS_PER_KW`]: crate::chain::chaininterface::MIN_RELAY_FEE_SAT_PER_1000_WEIGHT
1246+
/// [`FEERATE_FLOOR_SATS_PER_KW`]: crate::chain::chaininterface::INCREMENTAL_RELAY_FEE_SAT_PER_1000_WEIGHT
12471247
fn compute_fee_from_spent_amounts<F: Deref, L: Logger>(
12481248
input_amounts: u64, predicted_weight: u64, conf_target: ConfirmationTarget, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L
12491249
) -> Option<(u64, u64)>
@@ -1270,16 +1270,20 @@ fn compute_fee_from_spent_amounts<F: Deref, L: Logger>(
12701270
/// respect BIP125 rules 3) and 4) and if required adjust the new fee to meet the RBF policy
12711271
/// requirement.
12721272
fn feerate_bump<F: Deref, L: Logger>(
1273-
predicted_weight: u64, input_amounts: u64, previous_feerate: u64, feerate_strategy: &FeerateStrategy,
1274-
conf_target: ConfirmationTarget, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
1273+
predicted_weight: u64, input_amounts: u64, dust_limit_sats: u64, previous_feerate: u64,
1274+
feerate_strategy: &FeerateStrategy, conf_target: ConfirmationTarget,
1275+
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
12751276
) -> Option<(u64, u64)>
12761277
where
12771278
F::Target: FeeEstimator,
12781279
{
1280+
let previous_fee = previous_feerate * predicted_weight / 1000;
1281+
12791282
// If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee...
12801283
let (new_fee, new_feerate) = if let Some((new_fee, new_feerate)) =
12811284
compute_fee_from_spent_amounts(input_amounts, predicted_weight, conf_target, fee_estimator, logger)
12821285
{
1286+
log_debug!(logger, "Initiating fee rate bump from {} s/kWU ({} s) to {} s/kWU ({} s) using {:?} strategy", previous_feerate, previous_fee, new_feerate, new_fee, feerate_strategy);
12831287
match feerate_strategy {
12841288
FeerateStrategy::RetryPrevious => {
12851289
let previous_fee = previous_feerate * predicted_weight / 1000;
@@ -1297,15 +1301,12 @@ where
12971301
// ...else just increase the previous feerate by 25% (because that's a nice number)
12981302
let bumped_feerate = previous_feerate + (previous_feerate / 4);
12991303
let bumped_fee = bumped_feerate * predicted_weight / 1000;
1300-
if input_amounts <= bumped_fee {
1301-
log_warn!(logger, "Can't 25% bump new claiming tx, amount {} is too small", input_amounts);
1302-
return None;
1303-
}
1304+
13041305
(bumped_fee, bumped_feerate)
13051306
},
13061307
}
13071308
} else {
1308-
log_warn!(logger, "Can't new-estimation bump new claiming tx, amount {} is too small", input_amounts);
1309+
log_warn!(logger, "Can't bump new claiming tx, input amount {} is too small", input_amounts);
13091310
return None;
13101311
};
13111312

@@ -1316,22 +1317,31 @@ where
13161317
return Some((new_fee, new_feerate));
13171318
}
13181319

1319-
let previous_fee = previous_feerate * predicted_weight / 1000;
1320-
let min_relay_fee = MIN_RELAY_FEE_SAT_PER_1000_WEIGHT * predicted_weight / 1000;
1320+
let min_relay_fee = INCREMENTAL_RELAY_FEE_SAT_PER_1000_WEIGHT * predicted_weight / 1000;
13211321
// BIP 125 Opt-in Full Replace-by-Fee Signaling
13221322
// * 3. The replacement transaction pays an absolute fee of at least the sum paid by the original transactions.
13231323
// * 4. The replacement transaction must also pay for its own bandwidth at or above the rate set by the node's minimum relay fee setting.
1324-
let new_fee = if new_fee < previous_fee + min_relay_fee {
1325-
new_fee + previous_fee + min_relay_fee - new_fee
1326-
} else {
1327-
new_fee
1328-
};
1329-
Some((new_fee, new_fee * 1000 / predicted_weight))
1324+
let naive_new_fee = new_fee;
1325+
let new_fee = cmp::max(new_fee, previous_fee + min_relay_fee);
1326+
1327+
if new_fee > naive_new_fee {
1328+
log_debug!(logger, "Naive fee bump of {}s does not meet min relay fee requirements of {}s", naive_new_fee - previous_fee, min_relay_fee);
1329+
}
1330+
1331+
let remaining_output_amount = input_amounts.saturating_sub(new_fee);
1332+
if remaining_output_amount < dust_limit_sats {
1333+
log_warn!(logger, "Can't bump new claiming tx, output amount {} would end up below dust threshold {}", remaining_output_amount, dust_limit_sats);
1334+
return None;
1335+
}
1336+
1337+
let new_feerate = new_fee * 1000 / predicted_weight;
1338+
log_debug!(logger, "Fee rate bumped by {}s from {} s/KWU ({} s) to {} s/KWU ({} s)", new_fee - previous_fee, previous_feerate, previous_fee, new_feerate, new_fee);
1339+
Some((new_fee, new_feerate))
13301340
}
13311341

13321342
#[cfg(test)]
13331343
mod tests {
1334-
use crate::chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageTemplate, PackageSolvingData, RevokedHTLCOutput, RevokedOutput, WEIGHT_REVOKED_OUTPUT, weight_offered_htlc, weight_received_htlc};
1344+
use crate::chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageTemplate, PackageSolvingData, RevokedHTLCOutput, RevokedOutput, WEIGHT_REVOKED_OUTPUT, weight_offered_htlc, weight_received_htlc, feerate_bump};
13351345
use crate::chain::Txid;
13361346
use crate::ln::chan_utils::HTLCOutputInCommitment;
13371347
use crate::types::payment::{PaymentPreimage, PaymentHash};
@@ -1349,7 +1359,10 @@ mod tests {
13491359

13501360
use bitcoin::secp256k1::{PublicKey,SecretKey};
13511361
use bitcoin::secp256k1::Secp256k1;
1362+
use crate::chain::chaininterface::{ConfirmationTarget, FeeEstimator, FEERATE_FLOOR_SATS_PER_KW, LowerBoundedFeeEstimator};
1363+
use crate::chain::onchaintx::FeerateStrategy;
13521364
use crate::types::features::ChannelTypeFeatures;
1365+
use crate::util::test_utils::TestLogger;
13531366

13541367
fn fake_txid(n: u64) -> Txid {
13551368
Transaction {
@@ -1659,4 +1672,84 @@ mod tests {
16591672
}
16601673
}
16611674
}
1675+
1676+
struct TestFeeEstimator {
1677+
sat_per_kw: u32,
1678+
}
1679+
1680+
impl FeeEstimator for TestFeeEstimator {
1681+
fn get_est_sat_per_1000_weight(&self, _: ConfirmationTarget) -> u32 {
1682+
self.sat_per_kw
1683+
}
1684+
}
1685+
1686+
#[test]
1687+
fn test_feerate_bump() {
1688+
let sat_per_kw = FEERATE_FLOOR_SATS_PER_KW;
1689+
let test_fee_estimator = &TestFeeEstimator { sat_per_kw };
1690+
let fee_estimator = LowerBoundedFeeEstimator::new(test_fee_estimator);
1691+
let fee_rate_strategy = FeerateStrategy::ForceBump;
1692+
let confirmation_target = ConfirmationTarget::UrgentOnChainSweep;
1693+
1694+
{
1695+
// Check underflow doesn't occur
1696+
let predicted_weight_units = 1000;
1697+
let input_satoshis = 505;
1698+
1699+
let logger = TestLogger::new();
1700+
let bumped_fee_rate = feerate_bump(predicted_weight_units, input_satoshis, 546, 253, &fee_rate_strategy, confirmation_target, &fee_estimator, &logger);
1701+
assert!(bumped_fee_rate.is_none());
1702+
logger.assert_log_regex("lightning::chain::package", regex::Regex::new(r"Can't bump new claiming tx, input amount 505 is too small").unwrap(), 1);
1703+
}
1704+
1705+
{
1706+
// Check post-25%-bump-underflow scenario satisfying the following constraints:
1707+
// input - fee = 546
1708+
// input - fee * 1.25 = -1
1709+
1710+
// We accomplish that scenario with the following values:
1711+
// input = 2734
1712+
// fee = 2188
1713+
1714+
let predicted_weight_units = 1000;
1715+
let input_satoshis = 2734;
1716+
1717+
let logger = TestLogger::new();
1718+
let bumped_fee_rate = feerate_bump(predicted_weight_units, input_satoshis, 546, 2188, &fee_rate_strategy, confirmation_target, &fee_estimator, &logger);
1719+
assert!(bumped_fee_rate.is_none());
1720+
logger.assert_log_regex("lightning::chain::package", regex::Regex::new(r"Can't bump new claiming tx, output amount 0 would end up below dust threshold 546").unwrap(), 1);
1721+
}
1722+
1723+
{
1724+
// Check that an output amount of 0 is caught
1725+
let predicted_weight_units = 1000;
1726+
let input_satoshis = 506;
1727+
1728+
let logger = TestLogger::new();
1729+
let bumped_fee_rate = feerate_bump(predicted_weight_units, input_satoshis, 546, 253, &fee_rate_strategy, confirmation_target, &fee_estimator, &logger);
1730+
assert!(bumped_fee_rate.is_none());
1731+
logger.assert_log_regex("lightning::chain::package", regex::Regex::new(r"Can't bump new claiming tx, output amount 0 would end up below dust threshold 546").unwrap(), 1);
1732+
}
1733+
1734+
{
1735+
// Check that dust_threshold - 1 is blocked
1736+
let predicted_weight_units = 1000;
1737+
let input_satoshis = 1051;
1738+
1739+
let logger = TestLogger::new();
1740+
let bumped_fee_rate = feerate_bump(predicted_weight_units, input_satoshis, 546, 253, &fee_rate_strategy, confirmation_target, &fee_estimator, &logger);
1741+
assert!(bumped_fee_rate.is_none());
1742+
logger.assert_log_regex("lightning::chain::package", regex::Regex::new(r"Can't bump new claiming tx, output amount 545 would end up below dust threshold 546").unwrap(), 1);
1743+
}
1744+
1745+
{
1746+
let predicted_weight_units = 1000;
1747+
let input_satoshis = 1052;
1748+
1749+
let logger = TestLogger::new();
1750+
let bumped_fee_rate = feerate_bump(predicted_weight_units, input_satoshis, 546, 253, &fee_rate_strategy, confirmation_target, &fee_estimator, &logger).unwrap();
1751+
assert_eq!(bumped_fee_rate, (506, 506));
1752+
logger.assert_log_regex("lightning::chain::package", regex::Regex::new(r"Naive fee bump of 63s does not meet min relay fee requirements of 253s").unwrap(), 1);
1753+
}
1754+
}
16621755
}

lightning/src/ln/functional_tests.rs

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,18 +1307,22 @@ fn test_duplicate_htlc_different_direction_onchain() {
13071307

13081308
let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1);
13091309

1310+
// post-bump fee (288 satoshis) + dust threshold for output type (294 satoshis) = 582
1311+
let payment_value_sats = 582;
1312+
let payment_value_msats = payment_value_sats * 1000;
1313+
13101314
// balancing
13111315
send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
13121316

13131317
let (payment_preimage, payment_hash, ..) = route_payment(&nodes[0], &vec!(&nodes[1])[..], 900_000);
13141318

1315-
let (route, _, _, _) = get_route_and_payment_hash!(nodes[1], nodes[0], 800_000);
1319+
let (route, _, _, _) = get_route_and_payment_hash!(nodes[1], nodes[0], payment_value_msats);
13161320
let node_a_payment_secret = nodes[0].node.create_inbound_payment_for_hash(payment_hash, None, 7200, None).unwrap();
1317-
send_along_route_with_secret(&nodes[1], route, &[&[&nodes[0]]], 800_000, payment_hash, node_a_payment_secret);
1321+
send_along_route_with_secret(&nodes[1], route, &[&[&nodes[0]]], payment_value_msats, payment_hash, node_a_payment_secret);
13181322

13191323
// Provide preimage to node 0 by claiming payment
13201324
nodes[0].node.claim_funds(payment_preimage);
1321-
expect_payment_claimed!(nodes[0], payment_hash, 800_000);
1325+
expect_payment_claimed!(nodes[0], payment_hash, payment_value_msats);
13221326
check_added_monitors!(nodes[0], 1);
13231327

13241328
// Broadcast node 1 commitment txn
@@ -1327,7 +1331,7 @@ fn test_duplicate_htlc_different_direction_onchain() {
13271331
assert_eq!(remote_txn[0].output.len(), 4); // 1 local, 1 remote, 1 htlc inbound, 1 htlc outbound
13281332
let mut has_both_htlcs = 0; // check htlcs match ones committed
13291333
for outp in remote_txn[0].output.iter() {
1330-
if outp.value.to_sat() == 800_000 / 1000 {
1334+
if outp.value.to_sat() == payment_value_sats {
13311335
has_both_htlcs += 1;
13321336
} else if outp.value.to_sat() == 900_000 / 1000 {
13331337
has_both_htlcs += 1;
@@ -1347,18 +1351,15 @@ fn test_duplicate_htlc_different_direction_onchain() {
13471351
check_spends!(claim_txn[1], remote_txn[0]);
13481352
check_spends!(claim_txn[2], remote_txn[0]);
13491353
let preimage_tx = &claim_txn[0];
1350-
let (preimage_bump_tx, timeout_tx) = if claim_txn[1].input[0].previous_output == preimage_tx.input[0].previous_output {
1351-
(&claim_txn[1], &claim_txn[2])
1352-
} else {
1353-
(&claim_txn[2], &claim_txn[1])
1354-
};
1354+
let timeout_tx = claim_txn.iter().skip(1).find(|t| t.input[0].previous_output != preimage_tx.input[0].previous_output).unwrap();
1355+
let preimage_bump_tx = claim_txn.iter().skip(1).find(|t| t.input[0].previous_output == preimage_tx.input[0].previous_output).unwrap();
13551356

13561357
assert_eq!(preimage_tx.input.len(), 1);
13571358
assert_eq!(preimage_bump_tx.input.len(), 1);
13581359

13591360
assert_eq!(preimage_tx.input.len(), 1);
13601361
assert_eq!(preimage_tx.input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT); // HTLC 1 <--> 0, preimage tx
1361-
assert_eq!(remote_txn[0].output[preimage_tx.input[0].previous_output.vout as usize].value.to_sat(), 800);
1362+
assert_eq!(remote_txn[0].output[preimage_tx.input[0].previous_output.vout as usize].value.to_sat(), payment_value_sats);
13621363

13631364
assert_eq!(timeout_tx.input.len(), 1);
13641365
assert_eq!(timeout_tx.input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT); // HTLC 0 <--> 1, timeout tx
@@ -7923,22 +7924,31 @@ fn test_bump_penalty_txn_on_remote_commitment() {
79237924
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
79247925
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
79257926

7926-
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 59000000);
7927-
let (payment_preimage, payment_hash, ..) = route_payment(&nodes[0], &[&nodes[1]], 3_000_000);
7928-
route_payment(&nodes[1], &vec!(&nodes[0])[..], 3000000).0;
7929-
7930-
// Remote commitment txn with 4 outputs : to_local, to_remote, 1 outgoing HTLC, 1 incoming HTLC
7931-
let remote_txn = get_local_commitment_txn!(nodes[0], chan.2);
7932-
assert_eq!(remote_txn[0].output.len(), 4);
7933-
assert_eq!(remote_txn[0].input.len(), 1);
7934-
assert_eq!(remote_txn[0].input[0].previous_output.txid, chan.3.compute_txid());
7935-
7936-
// Claim a HTLC without revocation (provide B monitor with preimage)
7937-
nodes[1].node.claim_funds(payment_preimage);
7938-
expect_payment_claimed!(nodes[1], payment_hash, 3_000_000);
7939-
mine_transaction(&nodes[1], &remote_txn[0]);
7940-
check_added_monitors!(nodes[1], 2);
7941-
connect_blocks(&nodes[1], TEST_FINAL_CLTV); // Confirm blocks until the HTLC expires
7927+
let remote_txn = {
7928+
// post-bump fee (288 satoshis) + dust threshold for output type (294 satoshis) = 582
7929+
let htlc_value_a_msats = 582_000;
7930+
let htlc_value_b_msats = 583_000;
7931+
7932+
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 59000000);
7933+
let (payment_preimage, payment_hash, ..) = route_payment(&nodes[0], &[&nodes[1]], htlc_value_a_msats);
7934+
route_payment(&nodes[1], &vec!(&nodes[0])[..], htlc_value_b_msats);
7935+
7936+
// Remote commitment txn with 4 outputs : to_local, to_remote, 1 outgoing HTLC, 1 incoming HTLC
7937+
let remote_txn = get_local_commitment_txn!(nodes[0], chan.2);
7938+
assert_eq!(remote_txn[0].output.len(), 4);
7939+
assert_eq!(remote_txn[0].input.len(), 1);
7940+
assert_eq!(remote_txn[0].input[0].previous_output.txid, chan.3.compute_txid());
7941+
7942+
// Claim a HTLC without revocation (provide B monitor with preimage)
7943+
nodes[1].node.claim_funds(payment_preimage);
7944+
expect_payment_claimed!(nodes[1], payment_hash, htlc_value_a_msats);
7945+
mine_transaction(&nodes[1], &remote_txn[0]);
7946+
check_added_monitors!(nodes[1], 2);
7947+
connect_blocks(&nodes[1], TEST_FINAL_CLTV); // Confirm blocks until the HTLC expires
7948+
// depending on the block connection style, node 1 may have broadcast either 3 or 10 txs
7949+
7950+
remote_txn
7951+
};
79427952

79437953
// One or more claim tx should have been broadcast, check it
79447954
let timeout;

0 commit comments

Comments
 (0)