diff --git a/README.md b/README.md index 2ab14add81..099395a6e6 100644 --- a/README.md +++ b/README.md @@ -41,13 +41,12 @@ You will find detailed guides and frequently asked questions there. ### Configuring Bitcoin Core -:warning: Eclair requires Bitcoin Core 0.18.1, 0.19.1 or 0.20.1. If you are upgrading an existing wallet, you need to create a new address and send all your funds to that address. +:warning: Eclair requires Bitcoin Core 0.18.1, 0.19.1 or 0.20.1. If you are upgrading an existing wallet, you may need to create a new address and send all your funds to that address. Eclair needs a _synchronized_, _segwit-ready_, **_zeromq-enabled_**, _wallet-enabled_, _non-pruning_, _tx-indexing_ [Bitcoin Core](https://github.com/bitcoin/bitcoin) node. -Eclair will use any BTC it finds in the default Bitcoin Core wallet to fund any channels you choose to open. Eclair will return BTC from closed channels to this wallet. You can have multiple Bitcoin Core wallets but make sure that the default one is always available. -Any BTC found in the wallet can be used to fund the channels you choose to open and the BTC from closed channels will return to this wallet. -You can configure your Bitcoin Node to use either `p2sh-segwit` addresses or `bech32` addresses, Eclair is compatible with both modes. -If your Bitcoin Core wallet has "non-segwit UTXOs" (outputs that are neither `p2sh-segwit` or `bech32`), you must send them to a `p2sh-segwit` or `bech32` address. + +You can configure your Bitcoin node to use either `p2sh-segwit` addresses or `bech32` addresses, Eclair is compatible with both modes. +If your wallet has "non-segwit UTXOs" (outputs that are neither `p2sh-segwit` or `bech32`), you must send them to a `p2sh-segwit` or `bech32` address before running eclair. Run bitcoind with the following minimal `bitcoin.conf`: @@ -100,11 +99,22 @@ name | description eclair.bitcoind.rpcpassword | Bitcoin Core RPC password | bar eclair.bitcoind.zmqblock | Bitcoin Core ZMQ block address | "tcp://127.0.0.1:29000" eclair.bitcoind.zmqtx | Bitcoin Core ZMQ tx address | "tcp://127.0.0.1:29000" + eclair.bitcoind.wallet | Bitcoin Core wallet name | "" Quotes are not required unless the value contains special characters. Full syntax guide [here](https://github.com/lightbend/config/blob/master/HOCON.md). → see [`reference.conf`](eclair-core/src/main/resources/reference.conf) for full reference. There are many more options! +#### Configure Bitcoin Core wallet + +Eclair will use the default loaded Bitcoin Core wallet to fund any channels you choose to open. +If you want to use a different wallet from the default one, you must set `eclair.bitcoind.wallet` accordingly in your `eclair.conf`. + +:warning: Once a wallet is configured, you must be very careful if you want to change it: changing the wallet when you have channels open may result in a loss of funds (or a complex recovery procedure). + +Eclair will return BTC from closed channels to the wallet configured. +Any BTC found in the wallet can be used to fund the channels you choose to open. + #### Java Environment Variables Some advanced parameters can be changed with java environment variables. Most users won't need this and can skip this section. diff --git a/eclair-core/src/main/resources/reference.conf b/eclair-core/src/main/resources/reference.conf index 80bc4d0ccf..046dbe4e82 100644 --- a/eclair-core/src/main/resources/reference.conf +++ b/eclair-core/src/main/resources/reference.conf @@ -27,6 +27,10 @@ eclair { rpcport = 8332 rpcuser = "foo" rpcpassword = "bar" + // Name of the bitcoind wallet that should be used to fund channels. + // Once set you should NOT change it if your node has channels open, otherwise you may lose funds. + // NB: leave empty to automatically select the default loaded wallet. + wallet = "" zmqblock = "tcp://127.0.0.1:29000" zmqtx = "tcp://127.0.0.1:29000" } 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 b4572081e3..b420ae8daa 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala @@ -139,15 +139,20 @@ class Setup(datadir: File, val bitcoin = nodeParams.watcherType match { case BITCOIND => + val wallet = { + val name = config.getString("bitcoind.wallet") + if (!name.isBlank) Some(name) else None + } val bitcoinClient = new BasicBitcoinJsonRPCClient( user = config.getString("bitcoind.rpcuser"), password = config.getString("bitcoind.rpcpassword"), host = config.getString("bitcoind.host"), - port = config.getInt("bitcoind.rpcport")) + port = config.getInt("bitcoind.rpcport"), + wallet = wallet) val future = for { - json <- bitcoinClient.invoke("getblockchaininfo").recover { case _ => throw BitcoinRPCConnectionException } + json <- bitcoinClient.invoke("getblockchaininfo").recover { case e => throw BitcoinRPCConnectionException(e) } // Make sure wallet support is enabled in bitcoind. - _ <- bitcoinClient.invoke("getbalance").recover { case _ => throw BitcoinWalletDisabledException } + _ <- bitcoinClient.invoke("getbalance").recover { case e => throw BitcoinWalletDisabledException(e) } progress = (json \ "verificationprogress").extract[Double] ibd = (json \ "initialblockdownload").extract[Boolean] blocks = (json \ "blocks").extract[Long] @@ -396,9 +401,9 @@ object Kit { case object BitcoinZMQConnectionTimeoutException extends RuntimeException("could not connect to bitcoind using zeromq") -case object BitcoinRPCConnectionException extends RuntimeException("could not connect to bitcoind using json-rpc") +case class BitcoinRPCConnectionException(e: Throwable) extends RuntimeException("could not connect to bitcoind using json-rpc", e) -case object BitcoinWalletDisabledException extends RuntimeException("bitcoind must have wallet support enabled") +case class BitcoinWalletDisabledException(e: Throwable) extends RuntimeException("bitcoind wallet not available", e) case object EmptyAPIPasswordException extends RuntimeException("must set a password for the json-rpc api") diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/BasicBitcoinJsonRPCClient.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/BasicBitcoinJsonRPCClient.scala index c74b958735..b3d2cc336c 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/BasicBitcoinJsonRPCClient.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/BasicBitcoinJsonRPCClient.scala @@ -21,13 +21,13 @@ import com.softwaremill.sttp.json4s._ import fr.acinq.bitcoin.ByteVector32 import fr.acinq.eclair.KamonExt import fr.acinq.eclair.blockchain.Monitoring.{Metrics, Tags} -import org.json4s.{CustomSerializer, DefaultFormats} import org.json4s.JsonAST.{JString, JValue} import org.json4s.jackson.Serialization +import org.json4s.{CustomSerializer, DefaultFormats} import scala.concurrent.{ExecutionContext, Future} -class BasicBitcoinJsonRPCClient(user: String, password: String, host: String = "127.0.0.1", port: Int = 8332, ssl: Boolean = false)(implicit http: SttpBackend[Future, Nothing]) extends BitcoinJsonRPCClient { +class BasicBitcoinJsonRPCClient(user: String, password: String, host: String = "127.0.0.1", port: Int = 8332, ssl: Boolean = false, wallet: Option[String] = None)(implicit http: SttpBackend[Future, Nothing]) extends BitcoinJsonRPCClient { // necessary to properly serialize ByteVector32 into String readable by bitcoind object ByteVector32Serializer extends CustomSerializer[ByteVector32](_ => ( { @@ -35,9 +35,13 @@ class BasicBitcoinJsonRPCClient(user: String, password: String, host: String = " }, { case x: ByteVector32 => JString(x.toHex) })) + implicit val formats = DefaultFormats.withBigDecimal + ByteVector32Serializer private val scheme = if (ssl) "https" else "http" - private val serviceUri = uri"$scheme://$host:$port/wallet/" // wallet/ specifies to use the default bitcoind wallet, named "" + private val serviceUri = wallet match { + case Some(name) => uri"$scheme://$host:$port/wallet/$name" + case None => uri"$scheme://$host:$port" + } implicit val serialization = Serialization override def invoke(method: String, params: Any*)(implicit ec: ExecutionContext): Future[JValue] = diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/FxApp.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/FxApp.scala index 892d331072..733b10e0e9 100644 --- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/FxApp.scala +++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/FxApp.scala @@ -52,7 +52,7 @@ class FxApp extends Application with Logging { def onError(t: Throwable): Unit = t match { case e@TCPBindException(port) => notifyPreloader(new ErrorNotification("Setup", s"Could not bind to port $port", e)) - case e@BitcoinRPCConnectionException => + case e: BitcoinRPCConnectionException => notifyPreloader(new ErrorNotification("Setup", "Could not connect to Bitcoin Core using JSON-RPC.", e)) notifyPreloader(new AppNotification(InfoAppNotification, "Make sure that Bitcoin Core is up and running and RPC parameters are correct.")) case e@BitcoinZMQConnectionTimeoutException =>