Change RBF feerate bump rule to match BIP125 (#3298)
What changed, and why it matters
This commit fixes how Eclair increases transaction fees when replacing a funding transaction via RBF (Replace-By-Fee). Previously, Eclair only required the new fee rate to be 25/24 (about 4.17%) higher than the old one. Under recent Bitcoin Core defaults, that small bump can be too low for the network to accept the replacement. The fix adds a minimum flat bump of 25 sat/kw and uses whichever is larger. This is a protocol correctness / liveness fix rather than a theft-of-funds bug, but it can prevent channel funding from getting stuck.
Treat as a recommended protocol-compliance update. Nodes running interactive funding (splicing/RBF) should upgrade to avoid stuck replacement transactions. No immediate emergency response is warranted because funds are not directly at risk, but operators should monitor for failed RBF rounds on older versions.
Security signals we found
RBF fee-bump rule updated to satisfy Bitcoin Core relay policy
BOLT 2 spec alignment via minimum additive fee increment
Potential liveness issue: replacement transactions could be rejected by the network
No input validation, signature, or key-handling changes observed
Evidence from the diff
In InteractiveTxBuilder.scala, the minNextFeerate calculation for tx_init_rbf is changed from targetFeerate * 25 / 24 to max(targetFeerate * 25 / 24, targetFeerate + FeeratePerKw(25 sat)). This aligns Eclair with BOLT 2 PR 1327 and Bitcoin Core v30.0’s incrementalRelayFee default of 0.1 sat/vB (10 sat/kw). Without the additive floor, a 25/24 multiplicative bump could fall below the relay minimum, causing the replacement transaction to be rejected by the mempool and stalling interactive funding RBF attempts.
Changed components
eclair-core/src/main/scala/fr/acinq/eclair/channel/fund/InteractiveTxBuilder.scalaInteractive transaction funding RBF logicInspect captured patch +4 / −2
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fund/InteractiveTxBuilder.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fund/InteractiveTxBuilder.scala
index bf468c9..a87d978 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fund/InteractiveTxBuilder.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fund/InteractiveTxBuilder.scala
@@ -159,8 +159,10 @@ object InteractiveTxBuilder {
requireConfirmedInputs: RequireConfirmedInputs) {
/** The amount of the new funding output, which is the sum of the shared input, if any, and both sides' contributions. */
val fundingAmount: Satoshi = sharedInput_opt.map(_.info.txOut.amount).getOrElse(0 sat) + localContribution + remoteContribution
- // BOLT 2: MUST set `feerate` greater than or equal to 25/24 times the `feerate` of the previously constructed transaction, rounded down.
- val minNextFeerate: FeeratePerKw = targetFeerate * 25 / 24
+ // BOLT 2: MUST set `feerate` greater than or equal to the maximum of:
+ // - 25/24 times the `feerate` of the previously constructed transaction, rounded down.
+ // - 25 sat per kw greater than the `feerate` of the previously constructed transaction.
+ val minNextFeerate: FeeratePerKw = Seq(targetFeerate * 25 / 24, targetFeerate + FeeratePerKw(25 sat)).max
// BOLT 2: the initiator's serial IDs MUST use even values and the non-initiator odd values.
val serialIdParity: Int = if (isInitiator) 0 else 1
Why this scored 37/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.