What changed, and why it matters
This commit simply updates Eclair's required Bitcoin Core version from 28.1 to 29.1. It changes download URLs, version checks, documentation, and adjusts a couple of test expectations to match Bitcoin Core 29.1's behavior. There is no security vulnerability in this change.
No security action required. This is a routine dependency version bump. Operators should ensure their Bitcoin Core node is upgraded to 29.1 or higher before deploying this Eclair version, as required by the updated assertion.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit bumps the minimum supported Bitcoin Core version from 28.1 to 29.1. Changes include: updating bitcoind download URLs and SHA-256 hashes in pom.xml for Linux, macOS, and Windows; updating the version assertion in Setup.scala from 280100 to 290100; updating README and release notes; updating the test bitcoind path to bitcoin-29.1; and adjusting two test assertions to match Bitcoin Core 29.1’s default min relay fee (100 sat/kB) and mempool replacement behavior. The commit message explicitly states this release contains no breaking changes and is motivated by future feature leverage (orphan handling, ephemeral dust).
Changed components
Bitcoin Core integration/version gatingBuild configuration (pom.xml)Setup version checkDocumentation (README, release notes)Test infrastructure and test expectationsInspect captured patch +16 / −10
diff --git a/README.md b/README.md
index ddda013..74c8a74 100644
--- a/README.md
+++ b/README.md
@@ -62,7 +62,7 @@ This means that instead of re-implementing them, Eclair benefits from the verifi
* Eclair needs a _synchronized_, _segwit-ready_, **_zeromq-enabled_**, _wallet-enabled_, _non-pruning_, _tx-indexing_ [Bitcoin Core](https://github.com/bitcoin/bitcoin) node.
* You must configure your Bitcoin node to use `bech32` or `bech32m` (segwit) addresses. If your wallet has "non-segwit UTXOs" (outputs that are neither `p2sh-segwit`, `bech32` or `bech32m`), you must send them to a `bech32` or `bech32m` address before running Eclair.
-* Eclair requires Bitcoin Core 28.1 or higher. If you are upgrading an existing wallet, you may need to create a new address and send all your funds to that address.
+* Eclair requires Bitcoin Core 29.1 or higher. If you are upgrading an existing wallet, you may need to create a new address and send all your funds to that address.
Run bitcoind with the following minimal `bitcoin.conf`:
diff --git a/docs/release-notes/eclair-vnext.md b/docs/release-notes/eclair-vnext.md
index 22eaec3..144875b 100644
--- a/docs/release-notes/eclair-vnext.md
+++ b/docs/release-notes/eclair-vnext.md
@@ -6,6 +6,11 @@
<insert changes>
+### Update minimal version of Bitcoin Core
+
+With this release, eclair requires using Bitcoin Core 29.1.
+Newer versions of Bitcoin Core may be used, but have not been extensively tested.
+
### Configuration changes
<insert changes>
diff --git a/eclair-core/pom.xml b/eclair-core/pom.xml
index 5a757cf..5ff299f 100644
--- a/eclair-core/pom.xml
+++ b/eclair-core/pom.xml
@@ -87,8 +87,8 @@
<activeByDefault>true</activeByDefault>
</activation>
<properties>
- <bitcoind.url>https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-x86_64-linux-gnu.tar.gz</bitcoind.url>
- <bitcoind.sha256>07f77afd326639145b9ba9562912b2ad2ccec47b8a305bd075b4f4cb127b7ed7</bitcoind.sha256>
+ <bitcoind.url>https://bitcoincore.org/bin/bitcoin-core-29.1/bitcoin-29.1-x86_64-linux-gnu.tar.gz</bitcoind.url>
+ <bitcoind.sha256>2dddeaa8c0626ec446b6f21b64c0f3565a1e7e67ff0b586d25043cbd686c9455</bitcoind.sha256>
</properties>
</profile>
<profile>
@@ -99,8 +99,8 @@
</os>
</activation>
<properties>
- <bitcoind.url>https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-x86_64-apple-darwin.tar.gz</bitcoind.url>
- <bitcoind.sha256>c85d1a0ebedeff43b99db2c906b50f14547b84175a4d0ebb039a9809789af280</bitcoind.sha256>
+ <bitcoind.url>https://bitcoincore.org/bin/bitcoin-core-29.1/bitcoin-29.1-x86_64-apple-darwin.tar.gz</bitcoind.url>
+ <bitcoind.sha256>eed72e5ccbee0148bde65a00081f6dc3491bc60c0da641e698a9b8e0f1340b4a</bitcoind.sha256>
</properties>
</profile>
<profile>
@@ -111,8 +111,8 @@
</os>
</activation>
<properties>
- <bitcoind.url>https://bitcoincore.org/bin/bitcoin-core-28.1/bitcoin-28.1-win64.zip</bitcoind.url>
- <bitcoind.sha256>2d636ad562b347c96d36870d6ed810f4a364f446ca208258299f41048b35eab0</bitcoind.sha256>
+ <bitcoind.url>https://bitcoincore.org/bin/bitcoin-core-29.1/bitcoin-29.1-win64.zip</bitcoind.url>
+ <bitcoind.sha256>0cdabb828273319976de9a3c1aa34efe463c4d1c64d89b0b7e61634d6bbd39b7</bitcoind.sha256>
</properties>
</profile>
</profiles>
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala
index a11d8c1..e0e40ba 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala
@@ -195,7 +195,7 @@ class Setup(val datadir: File,
await(getBitcoinStatus(bitcoinClient), 30 seconds, "bitcoind did not respond after 30 seconds")
}
logger.info(s"bitcoind version=${bitcoinStatus.version}")
- assert(bitcoinStatus.version >= 280100, "Eclair requires Bitcoin Core 28.1 or higher")
+ assert(bitcoinStatus.version >= 290100, "Eclair requires Bitcoin Core 29.1 or higher")
bitcoinStatus.unspentAddresses.foreach { address =>
val isSegwit = addressToPublicKeyScript(bitcoinStatus.chainHash, address).map(script => Script.isNativeWitnessScript(script)).getOrElse(false)
assert(isSegwit, s"Your wallet contains non-segwit UTXOs (e.g. address=$address). You must send those UTXOs to a segwit address to use Eclair (check out our README for more details).")
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoindService.scala b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoindService.scala
index ae3de19..10ed32e 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoindService.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoindService.scala
@@ -62,7 +62,7 @@ trait BitcoindService extends Logging {
val PATH_BITCOIND = sys.env.get("BITCOIND_DIR") match {
case Some(customBitcoinDir) => new File(customBitcoinDir, "bitcoind")
- case None => new File(TestUtils.BUILD_DIRECTORY, "bitcoin-28.1/bin/bitcoind")
+ case None => new File(TestUtils.BUILD_DIRECTORY, "bitcoin-29.1/bin/bitcoind")
}
logger.info(s"using bitcoind: $PATH_BITCOIND")
val PATH_BITCOIND_DATADIR = new File(INTEGRATION_TMP_DIR, "datadir-bitcoin")
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/fee/BitcoinCoreFeeProviderSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/fee/BitcoinCoreFeeProviderSpec.scala
index 9031cf5..ab71ba2 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/fee/BitcoinCoreFeeProviderSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/fee/BitcoinCoreFeeProviderSpec.scala
@@ -95,7 +95,7 @@ class BitcoinCoreFeeProviderSpec extends TestKitBaseClass with BitcoindService w
regtestProvider.mempoolMinFee().pipeTo(sender.ref)
val mempoolMinFee = sender.expectMsgType[FeeratePerKB]
// The regtest provider doesn't have any transaction in its mempool, so it defaults to the min_relay_fee.
- assert(mempoolMinFee.feerate.toLong == FeeratePerKw.MinimumRelayFeeRate)
+ assert(mempoolMinFee.feerate.toLong == 100) // 0.1 sat/byte
}
private def createMockBitcoinClient(fees: Map[Int, FeeratePerKB], mempoolMinFee: FeeratePerKB): BasicBitcoinJsonRPCClient = {
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPublisherSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPublisherSpec.scala
index d4da4f2..14c7c04 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPublisherSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPublisherSpec.scala
@@ -1474,6 +1474,7 @@ class ReplaceableTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike w
// we try to publish the htlc-success again (can be caused by a node restart): it will fail to replace the existing
// one in the mempool but we must ensure we don't leave some utxos locked.
+ setFeerate(targetFeerate * 0.9)
val publisher2 = createPublisher()
publisher2 ! Publish(probe.ref, htlcSuccess)
val result = probe.expectMsgType[TxRejected]
Why this scored 15/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.