Remove deprecated `bip125 replaceable` field in mempool transaction class (#3319)
What changed, and why it matters
This commit removes support for a deprecated Bitcoin Core RPC field called `bip125-replaceable` from Eclair's internal mempool transaction model. Bitcoin Core stopped providing this field, so Eclair is updating its code to keep working with newer Bitcoin Core versions. It is a compatibility/maintenance change, not a fix for an active security vulnerability.
No immediate security action required. Operators should ensure they run compatible Bitcoin Core versions (v28+ with Full RBF default) and review any downstream logic that previously relied on the `replaceable` flag, as it is no longer available.
Security signals we found
Removes parsing of a deprecated Bitcoin Core RPC field
No cryptographic, authorization, or network-layer changes
No input validation or memory-safety changes
No references to CVEs, security advisories, or vulnerability reports in commit message
Evidence from the diff
The commit deletes the replaceable: Boolean field from BitcoinCoreClient.MempoolTx and stops parsing bip125-replaceable from Bitcoin Core’s getmempoolentry/getrawmempool JSON response. Tests that asserted mempoolCpfpTx.replaceable are also removed. The change is driven by Bitcoin Core v29+ removing the deprecated field and Full RBF being default since v28.
Changed components
eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/BitcoinCoreClient.scalaeclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreClientSpec.scalaInspect captured patch +2 / −7
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/BitcoinCoreClient.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/BitcoinCoreClient.scala
index 0a93000..e5af66f 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/BitcoinCoreClient.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/BitcoinCoreClient.scala
@@ -709,10 +709,9 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val lockUtxos: Bool
val JDecimal(fees) = json \ "fees" \ "base"
val JDecimal(ancestorFees) = json \ "fees" \ "ancestor"
val JDecimal(descendantFees) = json \ "fees" \ "descendant"
- val JBool(replaceable) = json \ "bip125-replaceable"
val unconfirmedParents = (json \ "depends").extract[List[String]].map(TxId.fromValidHex).toSet
// NB: bitcoind counts the transaction itself as its own ancestor and descendant, which is confusing: we fix that by decrementing these counters.
- MempoolTx(txid, vsize.toLong, weight.toLong, replaceable, toSatoshi(fees), ancestorCount.toInt - 1, toSatoshi(ancestorFees), descendantCount.toInt - 1, toSatoshi(descendantFees), unconfirmedParents)
+ MempoolTx(txid, vsize.toLong, weight.toLong, toSatoshi(fees), ancestorCount.toInt - 1, toSatoshi(ancestorFees), descendantCount.toInt - 1, toSatoshi(descendantFees), unconfirmedParents)
})
}
@@ -816,7 +815,6 @@ object BitcoinCoreClient {
* @param txid transaction id.
* @param vsize virtual transaction size as defined in BIP 141.
* @param weight transaction weight as defined in BIP 141.
- * @param replaceable Whether this transaction could be replaced with RBF (BIP125).
* @param fees transaction fees.
* @param ancestorCount number of unconfirmed parent transactions.
* @param ancestorFees transactions fees for the package consisting of this transaction and its unconfirmed parents.
@@ -824,7 +822,7 @@ object BitcoinCoreClient {
* @param descendantFees transactions fees for the package consisting of this transaction and its unconfirmed children (without its unconfirmed parents).
* @param unconfirmedParents unconfirmed transactions used as inputs for this transaction.
*/
- case class MempoolTx(txid: TxId, vsize: Long, weight: Long, replaceable: Boolean, fees: Satoshi, ancestorCount: Int, ancestorFees: Satoshi, descendantCount: Int, descendantFees: Satoshi, unconfirmedParents: Set[TxId])
+ case class MempoolTx(txid: TxId, vsize: Long, weight: Long, fees: Satoshi, ancestorCount: Int, ancestorFees: Satoshi, descendantCount: Int, descendantFees: Satoshi, unconfirmedParents: Set[TxId])
case class WalletTx(address: String, amount: Satoshi, fees: Satoshi, blockId_opt: Option[BlockId], confirmations: Long, txid: TxId, timestamp: Long)
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreClientSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreClientSpec.scala
index 875a614..5893751 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreClientSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreClientSpec.scala
@@ -1396,7 +1396,6 @@ class BitcoinCoreClientSpec extends TestKitBaseClass with BitcoindService with A
assert(mempoolCpfpTx.ancestorFees == mempoolCpfpTx.fees + mempoolTx.fees)
val expectedFees = Transactions.weight2fee(targetFeerate, tx.weight() + cpfpTx.weight())
assert(expectedFees * 0.95 <= mempoolCpfpTx.ancestorFees && mempoolCpfpTx.ancestorFees <= expectedFees * 1.05)
- assert(mempoolCpfpTx.replaceable)
generateBlocks(1)
}
@@ -1452,7 +1451,6 @@ class BitcoinCoreClientSpec extends TestKitBaseClass with BitcoindService with A
val packageWeight = fundingTx.weight() + mutualCloseTx.weight() + cpfpTx.weight()
val expectedFees = Transactions.weight2fee(targetFeerate, packageWeight)
assert(expectedFees * 0.95 <= mempoolCpfpTx.ancestorFees && mempoolCpfpTx.ancestorFees <= expectedFees * 1.05)
- assert(mempoolCpfpTx.replaceable)
generateBlocks(1)
}
@@ -1547,7 +1545,6 @@ class BitcoinCoreClientSpec extends TestKitBaseClass with BitcoindService with A
val packageWeight = txA1.weight() + txA2.weight() + txA3.weight() + txA4.weight() + txA5.weight() + txA6.weight() + txB1.weight() + txB2.weight() + txB3.weight() + cpfpTx.weight()
val expectedFees = Transactions.weight2fee(targetFeerate, packageWeight)
assert(expectedFees * 0.95 <= mempoolCpfpTx.ancestorFees && mempoolCpfpTx.ancestorFees <= expectedFees * 1.05)
- assert(mempoolCpfpTx.replaceable)
generateBlocks(1)
}
Why this scored 20/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.