From 05e21b1200131a547cd5876b4ca3553607089dc7 Mon Sep 17 00:00:00 2001 From: rorp Date: Fri, 22 Jan 2021 08:47:19 -0800 Subject: [PATCH] Bump Eclair version (#2405) * Bump Eclair version * cleanup * Bump JVM version for CI * Update docs * Fix docs --- .github/workflows/Linux_2.12_RPC_Tests.yml | 4 +- .github/workflows/Linux_2.13_RPC_Tests.yml | 2 + .github/workflows/Mac_2.13_RPC_Tests.yml | 2 + .../jsonmodels/eclair/EclairModels.scala | 48 +++++++----- .../commons/serializers/JsonReaders.scala | 49 +++++++++--- .../core/protocol/ln/LnInvoiceUnitTest.scala | 1 + .../ln/{ => channel}/ShortChannelIdTest.scala | 2 +- .../ln/{ => channel}/ShortChannelId.scala | 2 +- .../core/protocol/ln/routing/LnRoute.scala | 6 +- .../core/protocol/ln/routing/Route.scala | 13 ++++ docs/rpc/eclair.md | 10 +-- .../eclair/rpc/EclairRpcClientTest.scala | 66 ++++++++-------- eclair-rpc/eclair-rpc.sbt | 4 +- .../bitcoins/eclair/rpc/api/EclairApi.scala | 33 ++++---- .../eclair/rpc/client/EclairRpcClient.scala | 78 +++++++++++-------- .../org/bitcoins/node/NeutrinoNodeTest.scala | 12 +-- .../testkit/core/gen/ln/LnRouteGen.scala | 4 +- .../eclair/rpc/EclairRpcTestUtil.scala | 4 +- .../version-0.4.0/rpc/eclair.md | 2 +- 19 files changed, 202 insertions(+), 140 deletions(-) rename core-test/src/test/scala/org/bitcoins/core/protocol/ln/{ => channel}/ShortChannelIdTest.scala (97%) rename core/src/main/scala/org/bitcoins/core/protocol/ln/{ => channel}/ShortChannelId.scala (97%) create mode 100644 core/src/main/scala/org/bitcoins/core/protocol/ln/routing/Route.scala diff --git a/.github/workflows/Linux_2.12_RPC_Tests.yml b/.github/workflows/Linux_2.12_RPC_Tests.yml index 567e34d46e..eb1de9d2d5 100644 --- a/.github/workflows/Linux_2.12_RPC_Tests.yml +++ b/.github/workflows/Linux_2.12_RPC_Tests.yml @@ -1,4 +1,4 @@ -name: Linux 2.13 bitcoind and eclair rpc tests +name: Linux 2.12 bitcoind and eclair rpc tests env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} @@ -14,6 +14,8 @@ jobs: uses: actions/checkout@v2 - name: Setup Scala uses: olafurpg/setup-scala@v10 + with: + java-version: adopt@1.11 - name: Cache uses: actions/cache@v2 with: diff --git a/.github/workflows/Linux_2.13_RPC_Tests.yml b/.github/workflows/Linux_2.13_RPC_Tests.yml index 1dd1dbf631..1488d8158e 100644 --- a/.github/workflows/Linux_2.13_RPC_Tests.yml +++ b/.github/workflows/Linux_2.13_RPC_Tests.yml @@ -14,6 +14,8 @@ jobs: uses: actions/checkout@v2 - name: Setup Scala uses: olafurpg/setup-scala@v10 + with: + java-version: adopt@1.11 - name: Cache uses: actions/cache@v2 with: diff --git a/.github/workflows/Mac_2.13_RPC_Tests.yml b/.github/workflows/Mac_2.13_RPC_Tests.yml index 2d28729db0..26e5d58bf4 100644 --- a/.github/workflows/Mac_2.13_RPC_Tests.yml +++ b/.github/workflows/Mac_2.13_RPC_Tests.yml @@ -14,6 +14,8 @@ jobs: uses: actions/checkout@v2 - name: Setup Scala uses: olafurpg/setup-scala@v10 + with: + java-version: adopt@1.11 - name: Cache uses: actions/cache@v2 with: diff --git a/app-commons/src/main/scala/org/bitcoins/commons/jsonmodels/eclair/EclairModels.scala b/app-commons/src/main/scala/org/bitcoins/commons/jsonmodels/eclair/EclairModels.scala index 584380ed3d..0a7661e611 100644 --- a/app-commons/src/main/scala/org/bitcoins/commons/jsonmodels/eclair/EclairModels.scala +++ b/app-commons/src/main/scala/org/bitcoins/commons/jsonmodels/eclair/EclairModels.scala @@ -1,30 +1,24 @@ package org.bitcoins.commons.jsonmodels.eclair -import java.net.InetSocketAddress -import java.time.Instant -import java.util.UUID - import org.bitcoins.commons.serializers.JsonReaders._ import org.bitcoins.core.config.BitcoinNetwork import org.bitcoins.core.currency.Satoshis -import org.bitcoins.core.protocol.ln.channel.{ChannelState, FundedChannelId} +import org.bitcoins.core.protocol.ln.channel.{ + ChannelId, + ChannelState, + FundedChannelId, + ShortChannelId +} import org.bitcoins.core.protocol.ln.currency.MilliSatoshis import org.bitcoins.core.protocol.ln.fee.FeeProportionalMillionths import org.bitcoins.core.protocol.ln.node.{Feature, FeatureSupport, NodeId} -import org.bitcoins.core.protocol.ln.{ - LnHumanReadablePart, - PaymentPreimage, - ShortChannelId -} -import org.bitcoins.crypto.{ - DoubleSha256Digest, - DoubleSha256DigestBE, - ECDigitalSignature, - Sha256Digest, - StringFactory -} +import org.bitcoins.core.protocol.ln.{LnHumanReadablePart, PaymentPreimage} +import org.bitcoins.crypto._ import play.api.libs.json.JsObject +import java.net.InetSocketAddress +import java.time.Instant +import java.util.UUID import scala.concurrent.duration.FiniteDuration sealed abstract class EclairModels @@ -51,6 +45,23 @@ case class ChannelCommandResult( Either[ShortChannelId, FundedChannelId], State] ) + +case class UpdateRelayFeeResult( + results: Map[Either[ShortChannelId, FundedChannelId], UpdateRelayFee]) + +sealed trait UpdateRelayFee + +object UpdateRelayFee { + + case class OK( + channelId: ChannelId, + feeBaseMsat: MilliSatoshis, + feeProportionalMillionths: Long) + extends UpdateRelayFee + + case class Error(message: String) extends UpdateRelayFee +} + sealed trait State object ChannelCommandResult extends StringFactory[State] { @@ -343,7 +354,8 @@ object OutgoingPaymentStatus { completedAt: Instant //milliseconds ) extends OutgoingPaymentStatus - case class Failed(failures: Seq[PaymentFailure]) extends OutgoingPaymentStatus + case class Failed(failures: Seq[PaymentFailure], completedAt: Instant) + extends OutgoingPaymentStatus } case class PaymentFailure( diff --git a/app-commons/src/main/scala/org/bitcoins/commons/serializers/JsonReaders.scala b/app-commons/src/main/scala/org/bitcoins/commons/serializers/JsonReaders.scala index d6ac9e90ce..8a10bd5e17 100644 --- a/app-commons/src/main/scala/org/bitcoins/commons/serializers/JsonReaders.scala +++ b/app-commons/src/main/scala/org/bitcoins/commons/serializers/JsonReaders.scala @@ -1,11 +1,5 @@ package org.bitcoins.commons.serializers -import java.io.File -import java.net.{InetAddress, InetSocketAddress, URI} -import java.nio.file.Path -import java.time._ -import java.util.UUID - import org.bitcoins.commons.jsonmodels._ import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.LabelPurpose import org.bitcoins.commons.jsonmodels.bitcoind._ @@ -41,6 +35,11 @@ import org.bitcoins.core.wallet.fee.{BitcoinFeeUnit, SatoshisPerByte} import org.bitcoins.crypto._ import play.api.libs.json._ +import java.io.File +import java.net.{InetAddress, InetSocketAddress, URI} +import java.nio.file.Path +import java.time._ +import java.util.UUID import scala.concurrent.duration._ import scala.util.{Failure, Success, Try} @@ -910,6 +909,35 @@ object JsonReaders { SerializerUtil.buildJsErrorMsg("jsobject", err) } + implicit val updateRelayFeeResultReads: Reads[UpdateRelayFeeResult] = + Reads { + case obj: JsObject => + JsSuccess(UpdateRelayFeeResult(obj.value.map { x => + val channelId = Try(FundedChannelId.fromHex(x._1)) match { + case Success(id) => Right(id) + case Failure(_) => + Left(ShortChannelId.fromHumanReadableString(x._1)) + } + val result = Try( + UpdateRelayFee.OK( + channelId = FundedChannelId.fromHex( + (x._2 \ "channelId").validate[String].get), + feeBaseMsat = + (x._2 \ "cmd" \ "feeBase").validate[MilliSatoshis].get, + feeProportionalMillionths = + (x._2 \ "cmd" \ "feeProportionalMillionths").validate[Long].get + )) match { + case Success(ok) => ok + case Failure(_) => UpdateRelayFee.Error(x._2.toString()) + } + (channelId, result) + }.toMap)) + + case err @ (JsNull | _: JsBoolean | _: JsString | _: JsArray | + _: JsNumber) => + SerializerUtil.buildJsErrorMsg("jsobject", err) + } + implicit val channelUpdateReads: Reads[ChannelUpdate] = { Reads { jsValue => for { @@ -998,7 +1026,7 @@ object JsonReaders { implicit val paymentFailureTypeReads: Reads[PaymentFailure.Type] = Reads { jsValue => - (jsValue \ "name") + jsValue .validate[String] .flatMap { s => s.toLowerCase match { @@ -1251,10 +1279,13 @@ object JsonReaders { for { id <- (js \ "id").validate[PaymentId] paymentHash <- (js \ "paymentHash").validate[Sha256Digest] - failures <- (js \ "failures").validate[Vector[String]] + failures <- (js \ "failures").validate[Vector[JsObject]] timestamp <- (js \ "timestamp") .validate[Instant](instantReadsMilliseconds) - } yield WebSocketEvent.PaymentFailed(id, paymentHash, failures, timestamp) + } yield WebSocketEvent.PaymentFailed(id, + paymentHash, + failures.map(_.toString()), + timestamp) } implicit val paymentSentEventPartReads: Reads[ diff --git a/core-test/src/test/scala/org/bitcoins/core/protocol/ln/LnInvoiceUnitTest.scala b/core-test/src/test/scala/org/bitcoins/core/protocol/ln/LnInvoiceUnitTest.scala index 690d35ee42..bb1c9211a9 100644 --- a/core-test/src/test/scala/org/bitcoins/core/protocol/ln/LnInvoiceUnitTest.scala +++ b/core-test/src/test/scala/org/bitcoins/core/protocol/ln/LnInvoiceUnitTest.scala @@ -5,6 +5,7 @@ import org.bitcoins.core.protocol.ln.LnParams.{ LnBitcoinMainNet, LnBitcoinTestNet } +import org.bitcoins.core.protocol.ln.channel.ShortChannelId import org.bitcoins.core.protocol.ln.currency.{ MicroBitcoins, MilliBitcoins, diff --git a/core-test/src/test/scala/org/bitcoins/core/protocol/ln/ShortChannelIdTest.scala b/core-test/src/test/scala/org/bitcoins/core/protocol/ln/channel/ShortChannelIdTest.scala similarity index 97% rename from core-test/src/test/scala/org/bitcoins/core/protocol/ln/ShortChannelIdTest.scala rename to core-test/src/test/scala/org/bitcoins/core/protocol/ln/channel/ShortChannelIdTest.scala index 33e7397fb9..0a1acb7f95 100644 --- a/core-test/src/test/scala/org/bitcoins/core/protocol/ln/ShortChannelIdTest.scala +++ b/core-test/src/test/scala/org/bitcoins/core/protocol/ln/channel/ShortChannelIdTest.scala @@ -1,4 +1,4 @@ -package org.bitcoins.core.protocol.ln +package org.bitcoins.core.protocol.ln.channel import org.bitcoins.testkit.util.BitcoinSUnitTest diff --git a/core/src/main/scala/org/bitcoins/core/protocol/ln/ShortChannelId.scala b/core/src/main/scala/org/bitcoins/core/protocol/ln/channel/ShortChannelId.scala similarity index 97% rename from core/src/main/scala/org/bitcoins/core/protocol/ln/ShortChannelId.scala rename to core/src/main/scala/org/bitcoins/core/protocol/ln/channel/ShortChannelId.scala index c166225bc7..bb3a82215e 100644 --- a/core/src/main/scala/org/bitcoins/core/protocol/ln/ShortChannelId.scala +++ b/core/src/main/scala/org/bitcoins/core/protocol/ln/channel/ShortChannelId.scala @@ -1,4 +1,4 @@ -package org.bitcoins.core.protocol.ln +package org.bitcoins.core.protocol.ln.channel import org.bitcoins.core.number.UInt64 import org.bitcoins.crypto.{Factory, NetworkElement} diff --git a/core/src/main/scala/org/bitcoins/core/protocol/ln/routing/LnRoute.scala b/core/src/main/scala/org/bitcoins/core/protocol/ln/routing/LnRoute.scala index 6f01420bec..ee6ae2c44a 100644 --- a/core/src/main/scala/org/bitcoins/core/protocol/ln/routing/LnRoute.scala +++ b/core/src/main/scala/org/bitcoins/core/protocol/ln/routing/LnRoute.scala @@ -1,9 +1,7 @@ package org.bitcoins.core.protocol.ln.routing -import java.math.BigInteger - import org.bitcoins.core.number.UInt32 -import org.bitcoins.core.protocol.ln.ShortChannelId +import org.bitcoins.core.protocol.ln.channel.ShortChannelId import org.bitcoins.core.protocol.ln.currency.MilliSatoshis import org.bitcoins.core.protocol.ln.fee.{ FeeBaseMSat, @@ -13,6 +11,8 @@ import org.bitcoins.core.util.BytesUtil import org.bitcoins.crypto.{ECPublicKey, NetworkElement} import scodec.bits.ByteVector +import java.math.BigInteger + /** * Indicates a node to route through with specific options on the Lightning Network * For more details on these settings please see diff --git a/core/src/main/scala/org/bitcoins/core/protocol/ln/routing/Route.scala b/core/src/main/scala/org/bitcoins/core/protocol/ln/routing/Route.scala new file mode 100644 index 0000000000..d0c4a13e63 --- /dev/null +++ b/core/src/main/scala/org/bitcoins/core/protocol/ln/routing/Route.scala @@ -0,0 +1,13 @@ +package org.bitcoins.core.protocol.ln.routing + +import org.bitcoins.core.protocol.ln.channel.ShortChannelId +import org.bitcoins.core.protocol.ln.node.NodeId + +/** + * Represent differet types of LN routes. Supports node and channel routes. + */ +sealed trait Route + +case class NodeRoute(ids: Vector[NodeId]) extends Route + +case class ChannelRoute(ids: Vector[ShortChannelId]) extends Route diff --git a/docs/rpc/eclair.md b/docs/rpc/eclair.md index c345f9e6e9..3ad31c190b 100644 --- a/docs/rpc/eclair.md +++ b/docs/rpc/eclair.md @@ -5,7 +5,7 @@ title: Eclair This is a RPC client for [Eclair](https://github.com/acinq/eclair). It assumes that a bitcoind instance is running. -Currently this RPC client is written for [v0.4.1](https://github.com/ACINQ/eclair/releases/tag/v0.4.1) version of Eclair. +Currently this RPC client is written for [v0.5.0](https://github.com/ACINQ/eclair/releases/tag/v0.5.0) version of Eclair. ## Configuration of Eclair @@ -16,12 +16,12 @@ You can find the configuration we use for our testing infrastrture for eclair [h ## Starting Eclair -You need to download the jar from the [eclair's github](https://github.com/ACINQ/eclair/releases/tag/v0.4.1). +You need to download the jar from the [eclair's github](https://github.com/ACINQ/eclair/releases/tag/v0.5.0). -To run Eclair by unzipping the `eclair-node-0.4.1-e5fb281-bin.zip` and then running +To run Eclair by unzipping the `eclair-node-0.5.0-ac08560-bin.zip` and then running ```bash -$ ./eclair-node-0.4-69c538e/bin/eclair-node.sh +$ ./eclair-node-0.5.0-ac08560/bin/eclair-node.sh ``` If you wish to start Eclair from the RPC client, you can do one of the following: @@ -46,7 +46,7 @@ implicit val system = ActorSystem(s"eclair-rpc-${System.currentTimeMillis}") implicit val ec = system.dispatcher val datadirPath = Paths.get("path", "to", "datadir") -val binaryPath = Paths.get("path", "to", "eclair-node-0.3.3-12ac145.jar") +val binaryPath = Paths.get("path", "to", "eclair-node-0.5.0-ac08560", "bin", "eclair-node.sh") val instance = EclairInstance.fromDatadir(datadirPath.toFile,None) val client = new EclairRpcClient(instance, Some(binaryPath.toFile)) diff --git a/eclair-rpc-test/src/test/scala/org/bitcoins/eclair/rpc/EclairRpcClientTest.scala b/eclair-rpc-test/src/test/scala/org/bitcoins/eclair/rpc/EclairRpcClientTest.scala index b3a9c3e65c..2daee74d1a 100644 --- a/eclair-rpc-test/src/test/scala/org/bitcoins/eclair/rpc/EclairRpcClientTest.scala +++ b/eclair-rpc-test/src/test/scala/org/bitcoins/eclair/rpc/EclairRpcClientTest.scala @@ -1,7 +1,5 @@ package org.bitcoins.eclair.rpc -import java.nio.file.Files -import java.time.Instant import org.bitcoins.commons.jsonmodels.eclair._ import org.bitcoins.core.config.RegTest import org.bitcoins.core.currency.{CurrencyUnits, Satoshis} @@ -30,6 +28,8 @@ import org.bitcoins.testkit.eclair.rpc.{EclairNodes4, EclairRpcTestUtil} import org.bitcoins.testkit.util.{BitcoinSAsyncTest, EclairRpcTestClient} import org.scalatest.Assertion +import java.nio.file.Files +import java.time.Instant import scala.concurrent._ import scala.concurrent.duration.{DurationInt, _} @@ -64,38 +64,29 @@ class EclairRpcClientTest extends BitcoinSAsyncTest { } lazy val eclairNodesF: Future[EclairNodes4] = { - bitcoindRpcClientF.flatMap { bitcoindRpcClient => - val nodesF = EclairRpcTestUtil.createNodeLink(bitcoindRpcClient) - - val addedF = nodesF.map { nodes => - clients ++= List(nodes.c1, nodes.c2, nodes.c3, nodes.c4) - } - - addedF.flatMap(_ => nodesF) + for { + bitcoindRpcClient <- bitcoindRpcClientF + nodes <- EclairRpcTestUtil.createNodeLink(bitcoindRpcClient) + } yield { + clients ++= List(nodes.c1, nodes.c2, nodes.c3, nodes.c4) + nodes } } - lazy val firstClientF = eclairNodesF.map(_.c1) + lazy val firstClientF: Future[EclairRpcClient] = eclairNodesF.map(_.c1) - lazy val secondClientF = eclairNodesF.map(_.c2) + lazy val secondClientF: Future[EclairRpcClient] = eclairNodesF.map(_.c2) - lazy val thirdClientF = eclairNodesF.map(_.c3) + lazy val thirdClientF: Future[EclairRpcClient] = eclairNodesF.map(_.c3) - lazy val fourthClientF = eclairNodesF.map(_.c4) + lazy val fourthClientF: Future[EclairRpcClient] = eclairNodesF.map(_.c4) /** There is specific cases where we just need two clients, * so this is a helper val that pairs two connected * clients together with an open channel */ - lazy val clientOtherClientF = { - - //use second and third client above since they - //aren't really being used in the tests that use eclairNodesF - secondClientF.flatMap(s => thirdClientF.map(t => (s, t))) - } - - lazy val clientF = clientOtherClientF.map(_._1) - lazy val otherClientF = clientOtherClientF.map(_._2) + lazy val clientF: Future[EclairRpcClient] = secondClientF + lazy val otherClientF: Future[EclairRpcClient] = thirdClientF private val clients = Vector.newBuilder[EclairRpcClient] @@ -177,7 +168,7 @@ class EclairRpcClientTest extends BitcoinSAsyncTest { .exists(_.endsWith(".onion"))) route <- client1.findRoute(invoice, None) } yield { - route.size == 4 + route.ids.size == 4 }).recover { case err: RuntimeException if err.getMessage.contains("route not found") => @@ -196,7 +187,7 @@ class EclairRpcClientTest extends BitcoinSAsyncTest { .flatMap(_.getInfo) .flatMap(info => firstClientF.flatMap(_.findRoute(info.nodeId, MilliSatoshis(100)))) - .map(route => route.length == 4) + .map(route => route.ids.length == 4) .recover { case err: RuntimeException if err.getMessage.contains("route not found") => @@ -1026,18 +1017,23 @@ class EclairRpcClientTest extends BitcoinSAsyncTest { it should "update the relay fee of a channel" in { val channelAndFeeF = for { - channel <- EclairRpcTestUtil.openAndConfirmChannel(clientF, otherClientF) - feeOpt <- clientF.flatMap(_.channel(channel).map(_.feeBaseMsat)) + channelId <- + EclairRpcTestUtil.openAndConfirmChannel(clientF, otherClientF) + client <- clientF + channel <- client.channel(channelId) + feeOpt = channel.feeBaseMsat } yield { assert(feeOpt.isDefined) assert(feeOpt.get > MilliSatoshis.zero) - (channel, feeOpt.get) + (channelId, feeOpt.get) } for { - (channel, oldFee) <- channelAndFeeF - _ <- clientF.flatMap(_.updateRelayFee(channel, oldFee * 2, 1)) - newFeeOpt <- clientF.flatMap(_.channel(channel).map(_.feeBaseMsat)) + (channelId, oldFee) <- channelAndFeeF + client <- clientF + _ <- client.updateRelayFee(channelId, oldFee * 2, 1) + channel <- client.channel(channelId) + newFeeOpt = channel.feeBaseMsat } yield { assert(newFeeOpt.isDefined) assert(newFeeOpt.get == oldFee * 2) @@ -1058,9 +1054,11 @@ class EclairRpcClientTest extends BitcoinSAsyncTest { } for { + client <- clientF (channelId, shortChannelId, oldFee) <- channelAndFeeF - _ <- clientF.flatMap(_.updateRelayFee(shortChannelId, oldFee * 4, 1)) - newFeeOpt <- clientF.flatMap(_.channel(channelId).map(_.feeBaseMsat)) + _ <- client.updateRelayFee(shortChannelId, oldFee * 4, 1) + channel <- client.channel(channelId) + newFeeOpt = channel.feeBaseMsat } yield { assert(newFeeOpt.isDefined) assert(newFeeOpt.get == oldFee * 4) @@ -1192,7 +1190,7 @@ class EclairRpcClientTest extends BitcoinSAsyncTest { pending <- c.listPendingInvoices(from = None, to = None) } yield { assert(res.nonEmpty) - assert(pending.exists(_ == i)) + assert(pending.contains(i)) } } diff --git a/eclair-rpc/eclair-rpc.sbt b/eclair-rpc/eclair-rpc.sbt index b63d2496f2..cfb9ae3fd6 100644 --- a/eclair-rpc/eclair-rpc.sbt +++ b/eclair-rpc/eclair-rpc.sbt @@ -21,8 +21,8 @@ TaskKeys.downloadEclair := { Files.createDirectories(binaryDir) } - val version = "0.4.1" - val commit = "e5fb281" + val version = "0.5.0" + val commit = "ac08560" logger.debug(s"(Maybe) downloading Eclair binaries for version: $version") diff --git a/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/api/EclairApi.scala b/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/api/EclairApi.scala index 0c77d5d458..e2388034e6 100644 --- a/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/api/EclairApi.scala +++ b/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/api/EclairApi.scala @@ -1,25 +1,24 @@ package org.bitcoins.eclair.rpc.api -import java.net.InetSocketAddress -import java.time.Instant - import org.bitcoins.commons.jsonmodels.eclair._ import org.bitcoins.core.currency.{CurrencyUnit, Satoshis} -import org.bitcoins.core.protocol.ln.channel.{ChannelId, FundedChannelId} -import org.bitcoins.core.protocol.ln.currency.MilliSatoshis -import org.bitcoins.core.protocol.ln.node.NodeId -import org.bitcoins.core.protocol.ln.{ - LnInvoice, - LnParams, - PaymentPreimage, +import org.bitcoins.core.protocol.ln.channel.{ + ChannelId, + FundedChannelId, ShortChannelId } +import org.bitcoins.core.protocol.ln.currency.MilliSatoshis +import org.bitcoins.core.protocol.ln.node.NodeId +import org.bitcoins.core.protocol.ln.routing.{NodeRoute, Route} +import org.bitcoins.core.protocol.ln.{LnInvoice, LnParams, PaymentPreimage} import org.bitcoins.core.protocol.script.ScriptPubKey import org.bitcoins.core.protocol.{Address, BitcoinAddress} import org.bitcoins.core.wallet.fee.SatoshisPerByte import org.bitcoins.crypto.{DoubleSha256DigestBE, Sha256Digest} import org.bitcoins.eclair.rpc.network.NodeUri +import java.net.InetSocketAddress +import java.time.Instant import scala.concurrent.duration._ import scala.concurrent.{ExecutionContext, Future} @@ -77,15 +76,13 @@ trait EclairApi { def close(id: ChannelId, spk: ScriptPubKey): Future[ChannelCommandResult] - def findRoute( - nodeId: NodeId, - amountMsat: MilliSatoshis): Future[Vector[NodeId]] + def findRoute(nodeId: NodeId, amountMsat: MilliSatoshis): Future[NodeRoute] - def findRoute(invoice: LnInvoice): Future[Vector[NodeId]] + def findRoute(invoice: LnInvoice): Future[NodeRoute] def findRoute( invoice: LnInvoice, - amountMsat: MilliSatoshis): Future[Vector[NodeId]] + amountMsat: MilliSatoshis): Future[NodeRoute] def forceClose(channelId: ChannelId): Future[ChannelCommandResult] @@ -102,13 +99,13 @@ trait EclairApi { def updateRelayFee( channelId: ChannelId, feeBaseMsat: MilliSatoshis, - feePropertionalMillionths: Long): Future[ChannelCommandResult] + feeProportionalMillionths: Long): Future[UpdateRelayFeeResult] def updateRelayFee( shortChannelId: ShortChannelId, feeBaseMsat: MilliSatoshis, feePropertionalMillionths: Long - ): Future[ChannelCommandResult] + ): Future[UpdateRelayFeeResult] def open( nodeId: NodeId, @@ -264,7 +261,7 @@ trait EclairApi { */ def sendToRoute( invoice: LnInvoice, - route: scala.collection.immutable.Seq[NodeId], + route: Route, amountMsat: MilliSatoshis, paymentHash: Sha256Digest, finalCltvExpiry: Long, diff --git a/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/client/EclairRpcClient.scala b/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/client/EclairRpcClient.scala index 6812363fb7..641a6db6b1 100644 --- a/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/client/EclairRpcClient.scala +++ b/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/client/EclairRpcClient.scala @@ -1,10 +1,5 @@ package org.bitcoins.eclair.rpc.client -import java.io.File -import java.net.InetSocketAddress -import java.nio.file.NoSuchFileException -import java.time.Instant -import java.util.concurrent.atomic.AtomicInteger import akka.Done import akka.actor.ActorSystem import akka.http.javadsl.model.headers.HttpCredentials @@ -17,15 +12,15 @@ import akka.util.ByteString import org.bitcoins.commons.jsonmodels.eclair._ import org.bitcoins.commons.serializers.JsonReaders._ import org.bitcoins.core.currency.{CurrencyUnit, Satoshis} -import org.bitcoins.core.protocol.ln.channel.{ChannelId, FundedChannelId} -import org.bitcoins.core.protocol.ln.currency.MilliSatoshis -import org.bitcoins.core.protocol.ln.node.NodeId -import org.bitcoins.core.protocol.ln.{ - LnInvoice, - LnParams, - PaymentPreimage, +import org.bitcoins.core.protocol.ln.channel.{ + ChannelId, + FundedChannelId, ShortChannelId } +import org.bitcoins.core.protocol.ln.currency.MilliSatoshis +import org.bitcoins.core.protocol.ln.node.NodeId +import org.bitcoins.core.protocol.ln.routing.{ChannelRoute, NodeRoute, Route} +import org.bitcoins.core.protocol.ln.{LnInvoice, LnParams, PaymentPreimage} import org.bitcoins.core.protocol.script.ScriptPubKey import org.bitcoins.core.protocol.{Address, BitcoinAddress} import org.bitcoins.core.util.{BytesUtil, FutureUtil, StartStopAsync} @@ -39,6 +34,11 @@ import org.bitcoins.rpc.util.AsyncUtil import org.slf4j.LoggerFactory import play.api.libs.json._ +import java.io.File +import java.net.InetSocketAddress +import java.nio.file.NoSuchFileException +import java.time.Instant +import java.util.concurrent.atomic.AtomicInteger import scala.concurrent.duration.{DurationInt, FiniteDuration} import scala.concurrent.{ExecutionContext, Future, Promise} import scala.sys.process._ @@ -148,29 +148,30 @@ class EclairRpcClient( override def findRoute( nodeId: NodeId, - amountMsat: MilliSatoshis): Future[Vector[NodeId]] = { - eclairCall[Vector[NodeId]]("findroutetonode", - "nodeId" -> nodeId.toString, - "amountMsat" -> amountMsat.toBigDecimal.toString) + amountMsat: MilliSatoshis): Future[NodeRoute] = { + eclairCall[Vector[NodeId]]( + "findroutetonode", + "nodeId" -> nodeId.toString, + "amountMsat" -> amountMsat.toBigDecimal.toString).map(NodeRoute.apply) } - override def findRoute(invoice: LnInvoice): Future[Vector[NodeId]] = { + override def findRoute(invoice: LnInvoice): Future[NodeRoute] = { findRoute(invoice, None) } override def findRoute( invoice: LnInvoice, - amount: MilliSatoshis): Future[Vector[NodeId]] = { + amount: MilliSatoshis): Future[NodeRoute] = { findRoute(invoice, Some(amount)) } def findRoute( invoice: LnInvoice, - amountMsat: Option[MilliSatoshis]): Future[Vector[NodeId]] = { + amountMsat: Option[MilliSatoshis]): Future[NodeRoute] = { val params = Seq( Some("invoice" -> invoice.toString), amountMsat.map(x => "amountMsat" -> x.toBigDecimal.toString)).flatten - eclairCall[Vector[NodeId]]("findroute", params: _*) + eclairCall[Vector[NodeId]]("findroute", params: _*).map(NodeRoute.apply) } override def forceClose( @@ -511,16 +512,20 @@ class EclairRpcClient( def sendToRoute( invoice: LnInvoice, - route: scala.collection.immutable.Seq[NodeId], + route: Route, amountMsat: MilliSatoshis, paymentHash: Sha256Digest, finalCltvExpiry: Long, recipientAmountMsat: Option[MilliSatoshis], parentId: Option[PaymentId], externalId: Option[String]): Future[SendToRouteResult] = { + val ids = route match { + case NodeRoute(ids) => "nodeIds" -> ids.mkString(",") + case ChannelRoute(ids) => "shortChannelIds" -> ids.mkString(",") + } val params = Seq( "invoice" -> invoice.toString, - "route" -> route.iterator.mkString(","), + ids, "amountMsat" -> amountMsat.toBigDecimal.toString, "paymentHash" -> paymentHash.hex, "finalCltvExpiry" -> finalCltvExpiry.toString @@ -533,8 +538,8 @@ class EclairRpcClient( override def updateRelayFee( channelId: ChannelId, feeBaseMsat: MilliSatoshis, - feeProportionalMillionths: Long): Future[ChannelCommandResult] = { - eclairCall[ChannelCommandResult]( + feeProportionalMillionths: Long): Future[UpdateRelayFeeResult] = { + eclairCall[UpdateRelayFeeResult]( "updaterelayfee", "channelId" -> channelId.hex, "feeBaseMsat" -> feeBaseMsat.toLong.toString, @@ -545,8 +550,8 @@ class EclairRpcClient( override def updateRelayFee( shortChannelId: ShortChannelId, feeBaseMsat: MilliSatoshis, - feeProportionalMillionths: Long): Future[ChannelCommandResult] = { - eclairCall[ChannelCommandResult]( + feeProportionalMillionths: Long): Future[UpdateRelayFeeResult] = { + eclairCall[UpdateRelayFeeResult]( "updaterelayfee", "shortChannelId" -> shortChannelId.toHumanReadableString, "feeBaseMsat" -> feeBaseMsat.toLong.toString, @@ -904,11 +909,16 @@ class EclairRpcClient( val incoming: Sink[Message, Future[Done]] = Sink.foreach[Message] { case message: TextMessage.Strict => - val parsed: JsValue = Json.parse(message.text) - val validated: JsResult[WebSocketEvent] = - parsed.validate[WebSocketEvent] - val event = parseResult[WebSocketEvent](validated, parsed, "ws") - eventHandler(event) + try { + val parsed: JsValue = Json.parse(message.text) + val validated: JsResult[WebSocketEvent] = + parsed.validate[WebSocketEvent] + val event = parseResult[WebSocketEvent](validated, parsed, "ws") + eventHandler(event) + } catch { + case e: Throwable => + logger.error("Cannot process web-socket event", e) + } case _: Message => () } @@ -957,7 +967,7 @@ object EclairRpcClient { def apply( instance: EclairInstance, binary: Option[File] = None): EclairRpcClient = { - implicit val systme = ActorSystem.create(ActorSystemName) + implicit val system = ActorSystem.create(ActorSystemName) withActorSystem(instance, binary) } @@ -969,10 +979,10 @@ object EclairRpcClient { implicit system: ActorSystem) = new EclairRpcClient(instance, binary) /** The current commit we support of Eclair */ - private[bitcoins] val commit = "e5fb281" + private[bitcoins] val commit = "ac08560" /** The current version we support of Eclair */ - private[bitcoins] val version = "0.4.1" + private[bitcoins] val version = "0.5.0" /** The bitcoind version that eclair is officially tested & supported with by ACINQ * @see https://github.com/ACINQ/eclair/releases/tag/v0.4 diff --git a/node-test/src/test/scala/org/bitcoins/node/NeutrinoNodeTest.scala b/node-test/src/test/scala/org/bitcoins/node/NeutrinoNodeTest.scala index 1f9f4241a5..3d084ea36d 100644 --- a/node-test/src/test/scala/org/bitcoins/node/NeutrinoNodeTest.scala +++ b/node-test/src/test/scala/org/bitcoins/node/NeutrinoNodeTest.scala @@ -1,24 +1,16 @@ package org.bitcoins.node import akka.actor.Cancellable -import org.bitcoins.core.protocol.blockchain.Block -import org.bitcoins.core.protocol.script.ScriptPubKey import org.bitcoins.crypto.DoubleSha256DigestBE import org.bitcoins.rpc.client.common.BitcoindVersion -import org.bitcoins.rpc.util.RpcUtil import org.bitcoins.server.BitcoinSAppConfig import org.bitcoins.testkit.BitcoinSTestAppConfig import org.bitcoins.testkit.fixtures.UsesExperimentalBitcoind import org.bitcoins.testkit.node.fixture.NeutrinoNodeConnectedWithBitcoind -import org.bitcoins.testkit.node.{ - NeutrinoNodeFundedWalletBitcoind, - NodeTestUtil, - NodeUnitTest -} +import org.bitcoins.testkit.node.{NodeTestUtil, NodeUnitTest} import org.scalatest.FutureOutcome -import scala.concurrent.duration.DurationInt -import scala.concurrent.{Future, Promise} +import scala.concurrent.Future class NeutrinoNodeTest extends NodeUnitTest { diff --git a/testkit/src/main/scala/org/bitcoins/testkit/core/gen/ln/LnRouteGen.scala b/testkit/src/main/scala/org/bitcoins/testkit/core/gen/ln/LnRouteGen.scala index 28ed2e1109..0230a6f32f 100644 --- a/testkit/src/main/scala/org/bitcoins/testkit/core/gen/ln/LnRouteGen.scala +++ b/testkit/src/main/scala/org/bitcoins/testkit/core/gen/ln/LnRouteGen.scala @@ -1,13 +1,13 @@ package org.bitcoins.testkit.core.gen.ln -import org.bitcoins.testkit.core.gen.{CryptoGenerators, NumberGenerator} -import org.bitcoins.core.protocol.ln.ShortChannelId +import org.bitcoins.core.protocol.ln.channel.ShortChannelId import org.bitcoins.core.protocol.ln.currency.MilliSatoshis import org.bitcoins.core.protocol.ln.fee.{ FeeBaseMSat, FeeProportionalMillionths } import org.bitcoins.core.protocol.ln.routing.LnRoute +import org.bitcoins.testkit.core.gen.{CryptoGenerators, NumberGenerator} import org.scalacheck.Gen trait LnRouteGen { diff --git a/testkit/src/main/scala/org/bitcoins/testkit/eclair/rpc/EclairRpcTestUtil.scala b/testkit/src/main/scala/org/bitcoins/testkit/eclair/rpc/EclairRpcTestUtil.scala index 1061185358..29de750462 100644 --- a/testkit/src/main/scala/org/bitcoins/testkit/eclair/rpc/EclairRpcTestUtil.scala +++ b/testkit/src/main/scala/org/bitcoins/testkit/eclair/rpc/EclairRpcTestUtil.scala @@ -139,7 +139,9 @@ trait EclairRpcTestUtil extends BitcoinSLogger { "eclair.to-remote-delay-blocks" -> 144, "eclair.db.regtest.url" -> "jdbc:sqlite:regtest/", "eclair.max-payment-fee" -> 10, // avoid complaints about too high fees - "eclair.alias" -> "suredbits" + "eclair.alias" -> "suredbits", + "eclair.fulfill-safety-before-timeout-blocks" -> 1, + "eclair.min-final-expiry-delta-blocks" -> 2 ) } val c = ConfigFactory.parseMap(configMap.asJava) diff --git a/website/versioned_docs/version-0.4.0/rpc/eclair.md b/website/versioned_docs/version-0.4.0/rpc/eclair.md index ecabb4c5d7..e258698424 100644 --- a/website/versioned_docs/version-0.4.0/rpc/eclair.md +++ b/website/versioned_docs/version-0.4.0/rpc/eclair.md @@ -19,7 +19,7 @@ You can find the configuration we use for our testing infrastrture for eclair [h You need to download the jar from the [eclair's github](https://github.com/ACINQ/eclair/releases/tag/v0.4.1). -To run Eclair by unzipping the `eclair-node-0.4.1-e5fb281-bin.zip` and then running +To run Eclair by unzipping the `eclair-node-0.5.0-ac08560-bin.zip` and then running ```bash $ ./eclair-node-0.4-69c538e/bin/eclair-node.sh