Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Eclair 0.3.3 #1097

Merged
merged 2 commits into from Feb 12, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -1,6 +1,7 @@
package org.bitcoins.eclair.rpc

import java.nio.file.Files
import java.time.Instant

import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits, Satoshis}
import org.bitcoins.core.number.UInt64
Expand Down Expand Up @@ -31,7 +32,6 @@ import org.scalatest.Assertion

import scala.concurrent._
import scala.concurrent.duration.{DurationInt, _}
import scala.reflect.ClassTag

class EclairRpcClientTest extends BitcoinSAsyncTest {

Expand Down Expand Up @@ -153,6 +153,10 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
invoice <- client4.createInvoice("foo", 1000.msats)
info <- client4.getInfo
_ = assert(info.nodeId == invoice.nodeId)
_ = assert(
info.publicAddresses
.map(_.getHostString)
.exists(_.endsWith(".onion")))
route <- client1.findRoute(invoice, None)
} yield {
route.size == 4
Expand Down Expand Up @@ -533,12 +537,20 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
preimage = PaymentPreimage.random
invoice <- otherClient.createInvoice("foo", amt, preimage)
route <- client.findRoute(otherClientNodeId, amt)
paymentId <- client.sendToRoute(route,
amt,
invoice.lnTags.paymentHash.hash,
144,
Some("ext_id"))
_ <- EclairRpcTestUtil.awaitUntilPaymentSucceeded(client, paymentId)
result <- client.sendToRoute(invoice,
route,
amt,
invoice.lnTags.paymentHash.hash,
finalCltvExpiry = 144,
recipientAmountMsat = None,
parentId = None,
externalId = Some("ext_id"))
_ <- EclairRpcTestUtil
.awaitUntilIncomingPaymentStatus[IncomingPaymentStatus.Received](
otherClient,
invoice.lnTags.paymentHash.hash)
_ <- EclairRpcTestUtil.awaitUntilPaymentSucceeded(client,
result.parentId)
succeeded <- client.getSentInfo(invoice.lnTags.paymentHash.hash)
} yield {
assert(otherClientNodeId == invoice.nodeId)
Expand Down Expand Up @@ -937,11 +949,11 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {

// allupdates for a single node is broken in Eclair 0.3.2
// TODO remove recoverToPendingIf when https://github.com/ACINQ/eclair/issues/1179 is fixed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is fixed, can we remove it?

recoverToPendingIf[RuntimeException](sendPaymentsF.flatMap { _ =>
sendPaymentsF.flatMap { _ =>
executeSpecificClients(clientF = client1F,
otherClientF = client2F,
test = getChannelUpdates)
})
}

}

Expand Down Expand Up @@ -1034,31 +1046,16 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
})
}

def recoverToPendingIf[T <: AnyRef](future: Future[Any])(
implicit classTag: ClassTag[T]): Future[Assertion] = {

val clazz = classTag.runtimeClass
future.failed.transform(
ex =>
if (!clazz.isAssignableFrom(ex.getClass))
fail(s"Unknown exception ${ex}")
else pending,
ex => {
fail(s"Unexpected exception ${ex}")
}
)
}

it should "get updates for a single node" in {
// allupdates for a single node is broken in Eclair 0.3.2
// TODO remove recoverToPendingIf when https://github.com/ACINQ/eclair/issues/1179 is fixed
recoverToPendingIf[RuntimeException](for {
for {
client <- clientF
nodeInfo <- client.getInfo
updates <- client.allUpdates(nodeInfo.nodeId)
} yield {
assert(updates.nonEmpty)
})
}
}

it must "receive gossip messages about channel updates for nodes we do not have a direct channel with" in {
Expand Down Expand Up @@ -1100,12 +1097,11 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
//which is why i am choosing to use them for this test
// allupdates for a single node is broken in Eclair 0.3.2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this got fixed, can we remove it?

// TODO remove pendingUntilFixed when https://github.com/ACINQ/eclair/issues/1179 is fixed
recoverToPendingIf[RuntimeException](
executeSpecificClients(
clientF = secondClientF,
otherClientF = fourthClientF,
test = gossipFromPeerWithNoChannel
))
executeSpecificClients(
clientF = secondClientF,
otherClientF = fourthClientF,
test = gossipFromPeerWithNoChannel
)
}

it should "detect what network we are on" in {
Expand Down Expand Up @@ -1152,12 +1148,15 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
}
}

it should "list invoice" in {
it should "list invoices" in {
for {
c <- clientF
res <- c.listInvoices(from = None, to = None)
res <- c.listInvoices(from = None, to = Some(Instant.now()))
i <- c.createInvoice(description = "abc")
pending <- c.listPendingInvoices(from = None, to = None)
} yield {
assert(res.nonEmpty)
assert(pending.exists(_ == i))
}
}

Expand All @@ -1170,6 +1169,15 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
}
}

it should "get new address" in {
for {
c <- clientF
res <- c.getNewAddress()
} yield {
assert(res.toString.nonEmpty)
}
}

it should "disconnect node" in {
for {
c1 <- clientF
Expand Down
4 changes: 2 additions & 2 deletions eclair-rpc/eclair-rpc.sbt
Expand Up @@ -19,8 +19,8 @@ TaskKeys.downloadEclair := {
Files.createDirectories(binaryDir)
}

val version = "0.3.2"
val commit = "5ad3944"
val version = "0.3.3"
val commit = "12ac145"

logger.debug(s"(Maybe) downloading Eclair binaries for version: $version")

Expand Down
@@ -1,18 +1,21 @@
package org.bitcoins.eclair.rpc.api

import java.net.InetSocketAddress
import java.time.Instant

import org.bitcoins.core.crypto.Sha256Digest
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
import org.bitcoins.core.protocol.Address
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,
ShortChannelId
}
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.script.ScriptPubKey
import org.bitcoins.core.protocol.{Address, BitcoinAddress}
import org.bitcoins.core.wallet.fee.SatoshisPerByte
import org.bitcoins.eclair.rpc.network.NodeUri

Expand Down Expand Up @@ -42,9 +45,7 @@ trait EclairApi {
* @param from start timestamp
* @param to end timestamp
*/
def audit(
from: Option[FiniteDuration],
to: Option[FiniteDuration]): Future[AuditResult]
def audit(from: Option[Instant], to: Option[Instant]): Future[AuditResult]

def allUpdates(): Future[Vector[ChannelUpdate]]

Expand All @@ -67,6 +68,10 @@ trait EclairApi {

def connect(nodeId: NodeId, host: String, port: Int): Future[Unit]

def connect(nodeId: NodeId, addr: InetSocketAddress): Future[Unit]

def connect(nodeId: NodeId): Future[Unit]

def disconnect(nodeId: NodeId): Future[Unit]

def close(id: ChannelId, spk: ScriptPubKey): Future[Unit]
Expand Down Expand Up @@ -106,10 +111,11 @@ trait EclairApi {

def open(
nodeId: NodeId,
fundingSatoshis: CurrencyUnit,
funding: CurrencyUnit,
pushMsat: Option[MilliSatoshis],
feerateSatPerByte: Option[SatoshisPerByte],
channelFlags: Option[Byte]): Future[FundedChannelId]
channelFlags: Option[Byte],
openTimeout: Option[FiniteDuration]): Future[FundedChannelId]

/** The network that this [[org.bitcoins.eclair.rpc.api.EclairApi EclairApi]] is
* running on. This is not available directly from the eclair api, but is a very
Expand Down Expand Up @@ -171,8 +177,12 @@ trait EclairApi {
def getInvoice(paymentHash: Sha256Digest): Future[LnInvoice]

def listInvoices(
from: Option[FiniteDuration],
to: Option[FiniteDuration]): Future[Vector[LnInvoice]]
from: Option[Instant],
to: Option[Instant]): Future[Vector[LnInvoice]]

def listPendingInvoices(
from: Option[Instant],
to: Option[Instant]): Future[Vector[LnInvoice]]

def parseInvoice(invoice: LnInvoice): Future[InvoiceResult]

Expand Down Expand Up @@ -238,9 +248,7 @@ trait EclairApi {
def getReceivedInfo(
paymentHash: Sha256Digest): Future[Option[IncomingPayment]]

def getReceivedInfo(invoice: LnInvoice): Future[Option[IncomingPayment]] = {
getReceivedInfo(invoice.lnTags.paymentHash.hash)
}
def getReceivedInfo(invoice: LnInvoice): Future[Option[IncomingPayment]]

def sendToNode(
nodeId: NodeId,
Expand All @@ -255,14 +263,19 @@ trait EclairApi {
* Documented by not implemented in Eclair
*/
def sendToRoute(
invoice: LnInvoice,
route: scala.collection.immutable.Seq[NodeId],
amountMsat: MilliSatoshis,
paymentHash: Sha256Digest,
finalCltvExpiry: Long,
externalId: Option[String]): Future[PaymentId]
recipientAmountMsat: Option[MilliSatoshis],
parentId: Option[PaymentId],
externalId: Option[String]): Future[SendToRouteResult]

def usableBalances(): Future[Vector[UsableBalancesResult]]

/** Connects to the Eclair web socket end point and passes [[WebSocketEvent]]s to the given [[eventHandler]] */
def connectToWebSocket(eventHandler: WebSocketEvent => Unit): Future[Unit]

def getNewAddress(): Future[BitcoinAddress]
}
@@ -1,5 +1,6 @@
package org.bitcoins.eclair.rpc.api

import java.net.InetSocketAddress
import java.util.UUID

import org.bitcoins.core.crypto.{
Expand Down Expand Up @@ -30,7 +31,7 @@ case class GetInfoResult(
alias: String,
chainHash: DoubleSha256Digest,
blockHeight: Long,
publicAddresses: Seq[String])
publicAddresses: Seq[InetSocketAddress])

case class PeerInfo(
nodeId: NodeId,
Expand Down Expand Up @@ -87,7 +88,7 @@ case class NodeInfo(
nodeId: NodeId,
rgbColor: String,
alias: String,
addresses: Vector[String])
addresses: Vector[InetSocketAddress])

case class ChannelDesc(shortChannelId: ShortChannelId, a: NodeId, b: NodeId)

Expand Down Expand Up @@ -148,6 +149,8 @@ case class SentPayment(
id: PaymentId,
paymentHash: Sha256Digest,
paymentPreimage: PaymentPreimage,
recipientAmount: MilliSatoshis,
recipientNodeId: NodeId,
parts: Vector[SentPayment.Part]
)

Expand Down Expand Up @@ -201,6 +204,8 @@ case class PaymentId(value: UUID) {
override def toString: String = value.toString
}

case class SendToRouteResult(paymentId: PaymentId, parentId: PaymentId)

case class PaymentRequest(
prefix: LnHumanReadablePart,
timestamp: FiniteDuration, //seconds
Expand All @@ -211,13 +216,32 @@ case class PaymentRequest(
expiry: FiniteDuration, //seconds
amount: Option[MilliSatoshis])

sealed trait PaymentType

object PaymentType {

case object Standard extends PaymentType
case object SwapIn extends PaymentType
case object SwapOut extends PaymentType

def fromString(str: String): PaymentType = str match {
case "Standard" => Standard
case "SwapIn" => SwapIn
case "SwapOut" => SwapOut
case _ => throw new RuntimeException(s"Unknown payment type `$str`")
}

}

case class OutgoingPayment(
id: PaymentId,
parentId: PaymentId,
externalId: Option[String],
paymentHash: Sha256Digest,
paymentType: PaymentType,
amount: MilliSatoshis,
targetNodeId: NodeId,
recipientAmount: MilliSatoshis,
recipientNodeId: NodeId,
createdAt: FiniteDuration, //milliseconds
paymentRequest: Option[PaymentRequest],
status: OutgoingPaymentStatus)
Expand Down