Bump Eclair version (#2405)

* Bump Eclair version

* cleanup

* Bump JVM version for CI

* Update docs

* Fix docs
This commit is contained in:
rorp 2021-01-22 08:47:19 -08:00 committed by GitHub
parent ddd47b1cf1
commit d2203f2359
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 202 additions and 140 deletions

View file

@ -1,4 +1,4 @@
name: Linux 2.13 bitcoind and eclair rpc tests name: Linux 2.12 bitcoind and eclair rpc tests
env: env:
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
@ -14,6 +14,8 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Setup Scala - name: Setup Scala
uses: olafurpg/setup-scala@v10 uses: olafurpg/setup-scala@v10
with:
java-version: adopt@1.11
- name: Cache - name: Cache
uses: actions/cache@v2 uses: actions/cache@v2
with: with:

View file

@ -14,6 +14,8 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Setup Scala - name: Setup Scala
uses: olafurpg/setup-scala@v10 uses: olafurpg/setup-scala@v10
with:
java-version: adopt@1.11
- name: Cache - name: Cache
uses: actions/cache@v2 uses: actions/cache@v2
with: with:

View file

@ -14,6 +14,8 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Setup Scala - name: Setup Scala
uses: olafurpg/setup-scala@v10 uses: olafurpg/setup-scala@v10
with:
java-version: adopt@1.11
- name: Cache - name: Cache
uses: actions/cache@v2 uses: actions/cache@v2
with: with:

View file

@ -1,30 +1,24 @@
package org.bitcoins.commons.jsonmodels.eclair 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.commons.serializers.JsonReaders._
import org.bitcoins.core.config.BitcoinNetwork import org.bitcoins.core.config.BitcoinNetwork
import org.bitcoins.core.currency.Satoshis 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.currency.MilliSatoshis
import org.bitcoins.core.protocol.ln.fee.FeeProportionalMillionths import org.bitcoins.core.protocol.ln.fee.FeeProportionalMillionths
import org.bitcoins.core.protocol.ln.node.{Feature, FeatureSupport, NodeId} import org.bitcoins.core.protocol.ln.node.{Feature, FeatureSupport, NodeId}
import org.bitcoins.core.protocol.ln.{ import org.bitcoins.core.protocol.ln.{LnHumanReadablePart, PaymentPreimage}
LnHumanReadablePart, import org.bitcoins.crypto._
PaymentPreimage,
ShortChannelId
}
import org.bitcoins.crypto.{
DoubleSha256Digest,
DoubleSha256DigestBE,
ECDigitalSignature,
Sha256Digest,
StringFactory
}
import play.api.libs.json.JsObject import play.api.libs.json.JsObject
import java.net.InetSocketAddress
import java.time.Instant
import java.util.UUID
import scala.concurrent.duration.FiniteDuration import scala.concurrent.duration.FiniteDuration
sealed abstract class EclairModels sealed abstract class EclairModels
@ -51,6 +45,23 @@ case class ChannelCommandResult(
Either[ShortChannelId, FundedChannelId], Either[ShortChannelId, FundedChannelId],
State] 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 sealed trait State
object ChannelCommandResult extends StringFactory[State] { object ChannelCommandResult extends StringFactory[State] {
@ -343,7 +354,8 @@ object OutgoingPaymentStatus {
completedAt: Instant //milliseconds completedAt: Instant //milliseconds
) extends OutgoingPaymentStatus ) extends OutgoingPaymentStatus
case class Failed(failures: Seq[PaymentFailure]) extends OutgoingPaymentStatus case class Failed(failures: Seq[PaymentFailure], completedAt: Instant)
extends OutgoingPaymentStatus
} }
case class PaymentFailure( case class PaymentFailure(

View file

@ -1,11 +1,5 @@
package org.bitcoins.commons.serializers 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._
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.LabelPurpose import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.LabelPurpose
import org.bitcoins.commons.jsonmodels.bitcoind._ import org.bitcoins.commons.jsonmodels.bitcoind._
@ -41,6 +35,11 @@ import org.bitcoins.core.wallet.fee.{BitcoinFeeUnit, SatoshisPerByte}
import org.bitcoins.crypto._ import org.bitcoins.crypto._
import play.api.libs.json._ 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.concurrent.duration._
import scala.util.{Failure, Success, Try} import scala.util.{Failure, Success, Try}
@ -910,6 +909,35 @@ object JsonReaders {
SerializerUtil.buildJsErrorMsg("jsobject", err) 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] = { implicit val channelUpdateReads: Reads[ChannelUpdate] = {
Reads { jsValue => Reads { jsValue =>
for { for {
@ -998,7 +1026,7 @@ object JsonReaders {
implicit val paymentFailureTypeReads: Reads[PaymentFailure.Type] = Reads { implicit val paymentFailureTypeReads: Reads[PaymentFailure.Type] = Reads {
jsValue => jsValue =>
(jsValue \ "name") jsValue
.validate[String] .validate[String]
.flatMap { s => .flatMap { s =>
s.toLowerCase match { s.toLowerCase match {
@ -1251,10 +1279,13 @@ object JsonReaders {
for { for {
id <- (js \ "id").validate[PaymentId] id <- (js \ "id").validate[PaymentId]
paymentHash <- (js \ "paymentHash").validate[Sha256Digest] paymentHash <- (js \ "paymentHash").validate[Sha256Digest]
failures <- (js \ "failures").validate[Vector[String]] failures <- (js \ "failures").validate[Vector[JsObject]]
timestamp <- (js \ "timestamp") timestamp <- (js \ "timestamp")
.validate[Instant](instantReadsMilliseconds) .validate[Instant](instantReadsMilliseconds)
} yield WebSocketEvent.PaymentFailed(id, paymentHash, failures, timestamp) } yield WebSocketEvent.PaymentFailed(id,
paymentHash,
failures.map(_.toString()),
timestamp)
} }
implicit val paymentSentEventPartReads: Reads[ implicit val paymentSentEventPartReads: Reads[

View file

@ -5,6 +5,7 @@ import org.bitcoins.core.protocol.ln.LnParams.{
LnBitcoinMainNet, LnBitcoinMainNet,
LnBitcoinTestNet LnBitcoinTestNet
} }
import org.bitcoins.core.protocol.ln.channel.ShortChannelId
import org.bitcoins.core.protocol.ln.currency.{ import org.bitcoins.core.protocol.ln.currency.{
MicroBitcoins, MicroBitcoins,
MilliBitcoins, MilliBitcoins,

View file

@ -1,4 +1,4 @@
package org.bitcoins.core.protocol.ln package org.bitcoins.core.protocol.ln.channel
import org.bitcoins.testkit.util.BitcoinSUnitTest import org.bitcoins.testkit.util.BitcoinSUnitTest

View file

@ -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.core.number.UInt64
import org.bitcoins.crypto.{Factory, NetworkElement} import org.bitcoins.crypto.{Factory, NetworkElement}

View file

@ -1,9 +1,7 @@
package org.bitcoins.core.protocol.ln.routing package org.bitcoins.core.protocol.ln.routing
import java.math.BigInteger
import org.bitcoins.core.number.UInt32 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.currency.MilliSatoshis
import org.bitcoins.core.protocol.ln.fee.{ import org.bitcoins.core.protocol.ln.fee.{
FeeBaseMSat, FeeBaseMSat,
@ -13,6 +11,8 @@ import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.{ECPublicKey, NetworkElement} import org.bitcoins.crypto.{ECPublicKey, NetworkElement}
import scodec.bits.ByteVector import scodec.bits.ByteVector
import java.math.BigInteger
/** /**
* Indicates a node to route through with specific options on the Lightning Network * Indicates a node to route through with specific options on the Lightning Network
* For more details on these settings please see * For more details on these settings please see

View file

@ -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

View file

@ -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. 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 ## Configuration of Eclair
@ -16,12 +16,12 @@ You can find the configuration we use for our testing infrastrture for eclair [h
## Starting Eclair ## 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 ```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: 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 implicit val ec = system.dispatcher
val datadirPath = Paths.get("path", "to", "datadir") 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 instance = EclairInstance.fromDatadir(datadirPath.toFile,None)
val client = new EclairRpcClient(instance, Some(binaryPath.toFile)) val client = new EclairRpcClient(instance, Some(binaryPath.toFile))

View file

@ -1,7 +1,5 @@
package org.bitcoins.eclair.rpc package org.bitcoins.eclair.rpc
import java.nio.file.Files
import java.time.Instant
import org.bitcoins.commons.jsonmodels.eclair._ import org.bitcoins.commons.jsonmodels.eclair._
import org.bitcoins.core.config.RegTest import org.bitcoins.core.config.RegTest
import org.bitcoins.core.currency.{CurrencyUnits, Satoshis} 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.bitcoins.testkit.util.{BitcoinSAsyncTest, EclairRpcTestClient}
import org.scalatest.Assertion import org.scalatest.Assertion
import java.nio.file.Files
import java.time.Instant
import scala.concurrent._ import scala.concurrent._
import scala.concurrent.duration.{DurationInt, _} import scala.concurrent.duration.{DurationInt, _}
@ -64,38 +64,29 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
} }
lazy val eclairNodesF: Future[EclairNodes4] = { lazy val eclairNodesF: Future[EclairNodes4] = {
bitcoindRpcClientF.flatMap { bitcoindRpcClient => for {
val nodesF = EclairRpcTestUtil.createNodeLink(bitcoindRpcClient) bitcoindRpcClient <- bitcoindRpcClientF
nodes <- EclairRpcTestUtil.createNodeLink(bitcoindRpcClient)
val addedF = nodesF.map { nodes => } yield {
clients ++= List(nodes.c1, nodes.c2, nodes.c3, nodes.c4) clients ++= List(nodes.c1, nodes.c2, nodes.c3, nodes.c4)
} nodes
addedF.flatMap(_ => nodesF)
} }
} }
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, /** There is specific cases where we just need two clients,
* so this is a helper val that pairs two connected * so this is a helper val that pairs two connected
* clients together with an open channel * clients together with an open channel
*/ */
lazy val clientOtherClientF = { lazy val clientF: Future[EclairRpcClient] = secondClientF
lazy val otherClientF: Future[EclairRpcClient] = thirdClientF
//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)
private val clients = private val clients =
Vector.newBuilder[EclairRpcClient] Vector.newBuilder[EclairRpcClient]
@ -177,7 +168,7 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
.exists(_.endsWith(".onion"))) .exists(_.endsWith(".onion")))
route <- client1.findRoute(invoice, None) route <- client1.findRoute(invoice, None)
} yield { } yield {
route.size == 4 route.ids.size == 4
}).recover { }).recover {
case err: RuntimeException case err: RuntimeException
if err.getMessage.contains("route not found") => if err.getMessage.contains("route not found") =>
@ -196,7 +187,7 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
.flatMap(_.getInfo) .flatMap(_.getInfo)
.flatMap(info => .flatMap(info =>
firstClientF.flatMap(_.findRoute(info.nodeId, MilliSatoshis(100)))) firstClientF.flatMap(_.findRoute(info.nodeId, MilliSatoshis(100))))
.map(route => route.length == 4) .map(route => route.ids.length == 4)
.recover { .recover {
case err: RuntimeException case err: RuntimeException
if err.getMessage.contains("route not found") => 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 { it should "update the relay fee of a channel" in {
val channelAndFeeF = for { val channelAndFeeF = for {
channel <- EclairRpcTestUtil.openAndConfirmChannel(clientF, otherClientF) channelId <-
feeOpt <- clientF.flatMap(_.channel(channel).map(_.feeBaseMsat)) EclairRpcTestUtil.openAndConfirmChannel(clientF, otherClientF)
client <- clientF
channel <- client.channel(channelId)
feeOpt = channel.feeBaseMsat
} yield { } yield {
assert(feeOpt.isDefined) assert(feeOpt.isDefined)
assert(feeOpt.get > MilliSatoshis.zero) assert(feeOpt.get > MilliSatoshis.zero)
(channel, feeOpt.get) (channelId, feeOpt.get)
} }
for { for {
(channel, oldFee) <- channelAndFeeF (channelId, oldFee) <- channelAndFeeF
_ <- clientF.flatMap(_.updateRelayFee(channel, oldFee * 2, 1)) client <- clientF
newFeeOpt <- clientF.flatMap(_.channel(channel).map(_.feeBaseMsat)) _ <- client.updateRelayFee(channelId, oldFee * 2, 1)
channel <- client.channel(channelId)
newFeeOpt = channel.feeBaseMsat
} yield { } yield {
assert(newFeeOpt.isDefined) assert(newFeeOpt.isDefined)
assert(newFeeOpt.get == oldFee * 2) assert(newFeeOpt.get == oldFee * 2)
@ -1058,9 +1054,11 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
} }
for { for {
client <- clientF
(channelId, shortChannelId, oldFee) <- channelAndFeeF (channelId, shortChannelId, oldFee) <- channelAndFeeF
_ <- clientF.flatMap(_.updateRelayFee(shortChannelId, oldFee * 4, 1)) _ <- client.updateRelayFee(shortChannelId, oldFee * 4, 1)
newFeeOpt <- clientF.flatMap(_.channel(channelId).map(_.feeBaseMsat)) channel <- client.channel(channelId)
newFeeOpt = channel.feeBaseMsat
} yield { } yield {
assert(newFeeOpt.isDefined) assert(newFeeOpt.isDefined)
assert(newFeeOpt.get == oldFee * 4) assert(newFeeOpt.get == oldFee * 4)
@ -1192,7 +1190,7 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
pending <- c.listPendingInvoices(from = None, to = None) pending <- c.listPendingInvoices(from = None, to = None)
} yield { } yield {
assert(res.nonEmpty) assert(res.nonEmpty)
assert(pending.exists(_ == i)) assert(pending.contains(i))
} }
} }

View file

@ -21,8 +21,8 @@ TaskKeys.downloadEclair := {
Files.createDirectories(binaryDir) Files.createDirectories(binaryDir)
} }
val version = "0.4.1" val version = "0.5.0"
val commit = "e5fb281" val commit = "ac08560"
logger.debug(s"(Maybe) downloading Eclair binaries for version: $version") logger.debug(s"(Maybe) downloading Eclair binaries for version: $version")

View file

@ -1,25 +1,24 @@
package org.bitcoins.eclair.rpc.api package org.bitcoins.eclair.rpc.api
import java.net.InetSocketAddress
import java.time.Instant
import org.bitcoins.commons.jsonmodels.eclair._ import org.bitcoins.commons.jsonmodels.eclair._
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis} import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
import org.bitcoins.core.protocol.ln.channel.{ChannelId, FundedChannelId} import org.bitcoins.core.protocol.ln.channel.{
import org.bitcoins.core.protocol.ln.currency.MilliSatoshis ChannelId,
import org.bitcoins.core.protocol.ln.node.NodeId FundedChannelId,
import org.bitcoins.core.protocol.ln.{
LnInvoice,
LnParams,
PaymentPreimage,
ShortChannelId 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.script.ScriptPubKey
import org.bitcoins.core.protocol.{Address, BitcoinAddress} import org.bitcoins.core.protocol.{Address, BitcoinAddress}
import org.bitcoins.core.wallet.fee.SatoshisPerByte import org.bitcoins.core.wallet.fee.SatoshisPerByte
import org.bitcoins.crypto.{DoubleSha256DigestBE, Sha256Digest} import org.bitcoins.crypto.{DoubleSha256DigestBE, Sha256Digest}
import org.bitcoins.eclair.rpc.network.NodeUri import org.bitcoins.eclair.rpc.network.NodeUri
import java.net.InetSocketAddress
import java.time.Instant
import scala.concurrent.duration._ import scala.concurrent.duration._
import scala.concurrent.{ExecutionContext, Future} import scala.concurrent.{ExecutionContext, Future}
@ -77,15 +76,13 @@ trait EclairApi {
def close(id: ChannelId, spk: ScriptPubKey): Future[ChannelCommandResult] def close(id: ChannelId, spk: ScriptPubKey): Future[ChannelCommandResult]
def findRoute( def findRoute(nodeId: NodeId, amountMsat: MilliSatoshis): Future[NodeRoute]
nodeId: NodeId,
amountMsat: MilliSatoshis): Future[Vector[NodeId]]
def findRoute(invoice: LnInvoice): Future[Vector[NodeId]] def findRoute(invoice: LnInvoice): Future[NodeRoute]
def findRoute( def findRoute(
invoice: LnInvoice, invoice: LnInvoice,
amountMsat: MilliSatoshis): Future[Vector[NodeId]] amountMsat: MilliSatoshis): Future[NodeRoute]
def forceClose(channelId: ChannelId): Future[ChannelCommandResult] def forceClose(channelId: ChannelId): Future[ChannelCommandResult]
@ -102,13 +99,13 @@ trait EclairApi {
def updateRelayFee( def updateRelayFee(
channelId: ChannelId, channelId: ChannelId,
feeBaseMsat: MilliSatoshis, feeBaseMsat: MilliSatoshis,
feePropertionalMillionths: Long): Future[ChannelCommandResult] feeProportionalMillionths: Long): Future[UpdateRelayFeeResult]
def updateRelayFee( def updateRelayFee(
shortChannelId: ShortChannelId, shortChannelId: ShortChannelId,
feeBaseMsat: MilliSatoshis, feeBaseMsat: MilliSatoshis,
feePropertionalMillionths: Long feePropertionalMillionths: Long
): Future[ChannelCommandResult] ): Future[UpdateRelayFeeResult]
def open( def open(
nodeId: NodeId, nodeId: NodeId,
@ -264,7 +261,7 @@ trait EclairApi {
*/ */
def sendToRoute( def sendToRoute(
invoice: LnInvoice, invoice: LnInvoice,
route: scala.collection.immutable.Seq[NodeId], route: Route,
amountMsat: MilliSatoshis, amountMsat: MilliSatoshis,
paymentHash: Sha256Digest, paymentHash: Sha256Digest,
finalCltvExpiry: Long, finalCltvExpiry: Long,

View file

@ -1,10 +1,5 @@
package org.bitcoins.eclair.rpc.client 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.Done
import akka.actor.ActorSystem import akka.actor.ActorSystem
import akka.http.javadsl.model.headers.HttpCredentials 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.jsonmodels.eclair._
import org.bitcoins.commons.serializers.JsonReaders._ import org.bitcoins.commons.serializers.JsonReaders._
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis} import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
import org.bitcoins.core.protocol.ln.channel.{ChannelId, FundedChannelId} import org.bitcoins.core.protocol.ln.channel.{
import org.bitcoins.core.protocol.ln.currency.MilliSatoshis ChannelId,
import org.bitcoins.core.protocol.ln.node.NodeId FundedChannelId,
import org.bitcoins.core.protocol.ln.{
LnInvoice,
LnParams,
PaymentPreimage,
ShortChannelId 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.script.ScriptPubKey
import org.bitcoins.core.protocol.{Address, BitcoinAddress} import org.bitcoins.core.protocol.{Address, BitcoinAddress}
import org.bitcoins.core.util.{BytesUtil, FutureUtil, StartStopAsync} import org.bitcoins.core.util.{BytesUtil, FutureUtil, StartStopAsync}
@ -39,6 +34,11 @@ import org.bitcoins.rpc.util.AsyncUtil
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import play.api.libs.json._ 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.duration.{DurationInt, FiniteDuration}
import scala.concurrent.{ExecutionContext, Future, Promise} import scala.concurrent.{ExecutionContext, Future, Promise}
import scala.sys.process._ import scala.sys.process._
@ -148,29 +148,30 @@ class EclairRpcClient(
override def findRoute( override def findRoute(
nodeId: NodeId, nodeId: NodeId,
amountMsat: MilliSatoshis): Future[Vector[NodeId]] = { amountMsat: MilliSatoshis): Future[NodeRoute] = {
eclairCall[Vector[NodeId]]("findroutetonode", eclairCall[Vector[NodeId]](
"nodeId" -> nodeId.toString, "findroutetonode",
"amountMsat" -> amountMsat.toBigDecimal.toString) "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) findRoute(invoice, None)
} }
override def findRoute( override def findRoute(
invoice: LnInvoice, invoice: LnInvoice,
amount: MilliSatoshis): Future[Vector[NodeId]] = { amount: MilliSatoshis): Future[NodeRoute] = {
findRoute(invoice, Some(amount)) findRoute(invoice, Some(amount))
} }
def findRoute( def findRoute(
invoice: LnInvoice, invoice: LnInvoice,
amountMsat: Option[MilliSatoshis]): Future[Vector[NodeId]] = { amountMsat: Option[MilliSatoshis]): Future[NodeRoute] = {
val params = Seq( val params = Seq(
Some("invoice" -> invoice.toString), Some("invoice" -> invoice.toString),
amountMsat.map(x => "amountMsat" -> x.toBigDecimal.toString)).flatten amountMsat.map(x => "amountMsat" -> x.toBigDecimal.toString)).flatten
eclairCall[Vector[NodeId]]("findroute", params: _*) eclairCall[Vector[NodeId]]("findroute", params: _*).map(NodeRoute.apply)
} }
override def forceClose( override def forceClose(
@ -511,16 +512,20 @@ class EclairRpcClient(
def sendToRoute( def sendToRoute(
invoice: LnInvoice, invoice: LnInvoice,
route: scala.collection.immutable.Seq[NodeId], route: Route,
amountMsat: MilliSatoshis, amountMsat: MilliSatoshis,
paymentHash: Sha256Digest, paymentHash: Sha256Digest,
finalCltvExpiry: Long, finalCltvExpiry: Long,
recipientAmountMsat: Option[MilliSatoshis], recipientAmountMsat: Option[MilliSatoshis],
parentId: Option[PaymentId], parentId: Option[PaymentId],
externalId: Option[String]): Future[SendToRouteResult] = { externalId: Option[String]): Future[SendToRouteResult] = {
val ids = route match {
case NodeRoute(ids) => "nodeIds" -> ids.mkString(",")
case ChannelRoute(ids) => "shortChannelIds" -> ids.mkString(",")
}
val params = Seq( val params = Seq(
"invoice" -> invoice.toString, "invoice" -> invoice.toString,
"route" -> route.iterator.mkString(","), ids,
"amountMsat" -> amountMsat.toBigDecimal.toString, "amountMsat" -> amountMsat.toBigDecimal.toString,
"paymentHash" -> paymentHash.hex, "paymentHash" -> paymentHash.hex,
"finalCltvExpiry" -> finalCltvExpiry.toString "finalCltvExpiry" -> finalCltvExpiry.toString
@ -533,8 +538,8 @@ class EclairRpcClient(
override def updateRelayFee( override def updateRelayFee(
channelId: ChannelId, channelId: ChannelId,
feeBaseMsat: MilliSatoshis, feeBaseMsat: MilliSatoshis,
feeProportionalMillionths: Long): Future[ChannelCommandResult] = { feeProportionalMillionths: Long): Future[UpdateRelayFeeResult] = {
eclairCall[ChannelCommandResult]( eclairCall[UpdateRelayFeeResult](
"updaterelayfee", "updaterelayfee",
"channelId" -> channelId.hex, "channelId" -> channelId.hex,
"feeBaseMsat" -> feeBaseMsat.toLong.toString, "feeBaseMsat" -> feeBaseMsat.toLong.toString,
@ -545,8 +550,8 @@ class EclairRpcClient(
override def updateRelayFee( override def updateRelayFee(
shortChannelId: ShortChannelId, shortChannelId: ShortChannelId,
feeBaseMsat: MilliSatoshis, feeBaseMsat: MilliSatoshis,
feeProportionalMillionths: Long): Future[ChannelCommandResult] = { feeProportionalMillionths: Long): Future[UpdateRelayFeeResult] = {
eclairCall[ChannelCommandResult]( eclairCall[UpdateRelayFeeResult](
"updaterelayfee", "updaterelayfee",
"shortChannelId" -> shortChannelId.toHumanReadableString, "shortChannelId" -> shortChannelId.toHumanReadableString,
"feeBaseMsat" -> feeBaseMsat.toLong.toString, "feeBaseMsat" -> feeBaseMsat.toLong.toString,
@ -904,11 +909,16 @@ class EclairRpcClient(
val incoming: Sink[Message, Future[Done]] = val incoming: Sink[Message, Future[Done]] =
Sink.foreach[Message] { Sink.foreach[Message] {
case message: TextMessage.Strict => case message: TextMessage.Strict =>
val parsed: JsValue = Json.parse(message.text) try {
val validated: JsResult[WebSocketEvent] = val parsed: JsValue = Json.parse(message.text)
parsed.validate[WebSocketEvent] val validated: JsResult[WebSocketEvent] =
val event = parseResult[WebSocketEvent](validated, parsed, "ws") parsed.validate[WebSocketEvent]
eventHandler(event) val event = parseResult[WebSocketEvent](validated, parsed, "ws")
eventHandler(event)
} catch {
case e: Throwable =>
logger.error("Cannot process web-socket event", e)
}
case _: Message => () case _: Message => ()
} }
@ -957,7 +967,7 @@ object EclairRpcClient {
def apply( def apply(
instance: EclairInstance, instance: EclairInstance,
binary: Option[File] = None): EclairRpcClient = { binary: Option[File] = None): EclairRpcClient = {
implicit val systme = ActorSystem.create(ActorSystemName) implicit val system = ActorSystem.create(ActorSystemName)
withActorSystem(instance, binary) withActorSystem(instance, binary)
} }
@ -969,10 +979,10 @@ object EclairRpcClient {
implicit system: ActorSystem) = new EclairRpcClient(instance, binary) implicit system: ActorSystem) = new EclairRpcClient(instance, binary)
/** The current commit we support of Eclair */ /** The current commit we support of Eclair */
private[bitcoins] val commit = "e5fb281" private[bitcoins] val commit = "ac08560"
/** The current version we support of Eclair */ /** 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 /** The bitcoind version that eclair is officially tested & supported with by ACINQ
* @see https://github.com/ACINQ/eclair/releases/tag/v0.4 * @see https://github.com/ACINQ/eclair/releases/tag/v0.4

View file

@ -1,24 +1,16 @@
package org.bitcoins.node package org.bitcoins.node
import akka.actor.Cancellable 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.crypto.DoubleSha256DigestBE
import org.bitcoins.rpc.client.common.BitcoindVersion import org.bitcoins.rpc.client.common.BitcoindVersion
import org.bitcoins.rpc.util.RpcUtil
import org.bitcoins.server.BitcoinSAppConfig import org.bitcoins.server.BitcoinSAppConfig
import org.bitcoins.testkit.BitcoinSTestAppConfig import org.bitcoins.testkit.BitcoinSTestAppConfig
import org.bitcoins.testkit.fixtures.UsesExperimentalBitcoind import org.bitcoins.testkit.fixtures.UsesExperimentalBitcoind
import org.bitcoins.testkit.node.fixture.NeutrinoNodeConnectedWithBitcoind import org.bitcoins.testkit.node.fixture.NeutrinoNodeConnectedWithBitcoind
import org.bitcoins.testkit.node.{ import org.bitcoins.testkit.node.{NodeTestUtil, NodeUnitTest}
NeutrinoNodeFundedWalletBitcoind,
NodeTestUtil,
NodeUnitTest
}
import org.scalatest.FutureOutcome import org.scalatest.FutureOutcome
import scala.concurrent.duration.DurationInt import scala.concurrent.Future
import scala.concurrent.{Future, Promise}
class NeutrinoNodeTest extends NodeUnitTest { class NeutrinoNodeTest extends NodeUnitTest {

View file

@ -1,13 +1,13 @@
package org.bitcoins.testkit.core.gen.ln package org.bitcoins.testkit.core.gen.ln
import org.bitcoins.testkit.core.gen.{CryptoGenerators, NumberGenerator} import org.bitcoins.core.protocol.ln.channel.ShortChannelId
import org.bitcoins.core.protocol.ln.ShortChannelId
import org.bitcoins.core.protocol.ln.currency.MilliSatoshis import org.bitcoins.core.protocol.ln.currency.MilliSatoshis
import org.bitcoins.core.protocol.ln.fee.{ import org.bitcoins.core.protocol.ln.fee.{
FeeBaseMSat, FeeBaseMSat,
FeeProportionalMillionths FeeProportionalMillionths
} }
import org.bitcoins.core.protocol.ln.routing.LnRoute import org.bitcoins.core.protocol.ln.routing.LnRoute
import org.bitcoins.testkit.core.gen.{CryptoGenerators, NumberGenerator}
import org.scalacheck.Gen import org.scalacheck.Gen
trait LnRouteGen { trait LnRouteGen {

View file

@ -139,7 +139,9 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
"eclair.to-remote-delay-blocks" -> 144, "eclair.to-remote-delay-blocks" -> 144,
"eclair.db.regtest.url" -> "jdbc:sqlite:regtest/", "eclair.db.regtest.url" -> "jdbc:sqlite:regtest/",
"eclair.max-payment-fee" -> 10, // avoid complaints about too high fees "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) val c = ConfigFactory.parseMap(configMap.asJava)

View file

@ -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). 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 ```bash
$ ./eclair-node-0.4-69c538e/bin/eclair-node.sh $ ./eclair-node-0.4-69c538e/bin/eclair-node.sh