Reduce number of requests we send to eclair in testkits, add commandN… (#343)

* Reduce number of requests we send to eclair in testkits, add commandName param to parseResult in EclairRpcClient

* Factor our EclairRpcClient error message

* Make jvm opts more sane, refactor error message code again

* Add error message on EclairRpcTestUtil.sendPayments, scalafmt
This commit is contained in:
Chris Stewart 2019-02-19 14:18:37 -06:00 committed by GitHub
parent abc6f55a08
commit 88e6450c5a
4 changed files with 293 additions and 226 deletions

View file

@ -1,2 +1 @@
-Xmx1024m -Xmx1024m
-Xms1024m

View file

@ -7,7 +7,11 @@ import org.bitcoins.core.config.RegTest
import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits, Satoshis} import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits, Satoshis}
import org.bitcoins.core.number.Int64 import org.bitcoins.core.number.Int64
import org.bitcoins.core.protocol.ln.LnParams.LnBitcoinRegTest import org.bitcoins.core.protocol.ln.LnParams.LnBitcoinRegTest
import org.bitcoins.core.protocol.ln.channel.{ChannelId, ChannelState, FundedChannelId} import org.bitcoins.core.protocol.ln.channel.{
ChannelId,
ChannelState,
FundedChannelId
}
import org.bitcoins.core.protocol.ln.currency._ import org.bitcoins.core.protocol.ln.currency._
import org.bitcoins.core.protocol.ln.node.NodeId import org.bitcoins.core.protocol.ln.node.NodeId
import org.bitcoins.core.util.BitcoinSLogger import org.bitcoins.core.util.BitcoinSLogger
@ -41,7 +45,6 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
blocksF.flatMap(_ => cliF) blocksF.flatMap(_ => cliF)
} }
val eclairNodesF: Future[EclairNodes4] = { val eclairNodesF: Future[EclairNodes4] = {
bitcoindRpcClientF.flatMap { bitcoindRpcClient => bitcoindRpcClientF.flatMap { bitcoindRpcClient =>
val nodesF = EclairRpcTestUtil.createNodeLink(bitcoindRpcClient) val nodesF = EclairRpcTestUtil.createNodeLink(bitcoindRpcClient)
@ -62,7 +65,6 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
lazy val fourthClientF = eclairNodesF.map(_.c4) lazy val fourthClientF = 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
@ -71,7 +73,7 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
//use second and third client above since they //use second and third client above since they
//aren't really being used in the tests that use eclairNodesF //aren't really being used in the tests that use eclairNodesF
secondClientF.flatMap(s => thirdClientF.map(t => (s,t))) secondClientF.flatMap(s => thirdClientF.map(t => (s, t)))
} }
lazy val clientF = clientOtherClientF.map(_._1) lazy val clientF = clientOtherClientF.map(_._1)
@ -86,21 +88,22 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
* @tparam T * @tparam T
* @return * @return
*/ */
def executeWithClientOtherClient[T](test: (EclairRpcClient,EclairRpcClient) => Future[T]): Future[T] = { def executeWithClientOtherClient[T](
test: (EclairRpcClient, EclairRpcClient) => Future[T]): Future[T] = {
executeSpecificClients(clientF = clientF, executeSpecificClients(clientF = clientF,
otherClientF = otherClientF, otherClientF = otherClientF,
test = test) test = test)
} }
/** Executes the test with the clients passed as a parameter */ /** Executes the test with the clients passed as a parameter */
def executeSpecificClients[T](clientF: Future[EclairRpcClient], def executeSpecificClients[T](
otherClientF: Future[EclairRpcClient], clientF: Future[EclairRpcClient],
test: (EclairRpcClient,EclairRpcClient) => Future[T]): Future[T] = { otherClientF: Future[EclairRpcClient],
test: (EclairRpcClient, EclairRpcClient) => Future[T]): Future[T] = {
clientF.flatMap { c1 => clientF.flatMap { c1 =>
otherClientF.flatMap { other => otherClientF.flatMap { other =>
test(c1,other) test(c1, other)
} }
} }
} }
@ -112,43 +115,43 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
val changeAddrF = bitcoindRpcClientF.flatMap(_.getNewAddress()) val changeAddrF = bitcoindRpcClientF.flatMap(_.getNewAddress())
val result: Future[Assertion] = { val result: Future[Assertion] = {
val isOpenedF: Future[(ChannelId, Assertion)] = { val isOpenedF: Future[(ChannelId, Assertion)] = {
val getChannelId = (client: EclairRpcClient, otherClient: EclairRpcClient) => { val getChannelId =
otherClient.getInfo.flatMap { info => (client: EclairRpcClient, otherClient: EclairRpcClient) => {
val amt = Satoshis(Int64(100000)) otherClient.getInfo.flatMap { info =>
val openedChanF = clientF.flatMap(_.open(info.nodeId, amt)) val amt = Satoshis(Int64(100000))
val openedChanF = clientF.flatMap(_.open(info.nodeId, amt))
openedChanF.flatMap { channelId => openedChanF.flatMap { channelId =>
val exists = hasChannel(client, channelId) val exists = hasChannel(client, channelId)
exists.map(e => (channelId, e)) exists.map(e => (channelId, e))
}
} }
} }
} executeWithClientOtherClient[(ChannelId, Assertion)](getChannelId)
executeWithClientOtherClient[(ChannelId,Assertion)](getChannelId)
} }
val isConfirmedF: Future[(ChannelId, Assertion)] = { val isConfirmedF: Future[(ChannelId, Assertion)] = {
val getIsConfirmed = { val getIsConfirmed = { (client: EclairRpcClient, _: EclairRpcClient) =>
(client: EclairRpcClient, _: EclairRpcClient) => isOpenedF.flatMap {
isOpenedF.flatMap { case (chanId, assertion) =>
case (chanId, assertion) => val generatedF = bitcoindRpcClientF.flatMap(_.generate(6))
val generatedF = bitcoindRpcClientF.flatMap(_.generate(6)) val normalF = generatedF.flatMap { _ =>
val normalF = generatedF.flatMap { _ => EclairRpcTestUtil.awaitUntilChannelNormal(
EclairRpcTestUtil.awaitUntilChannelNormal( client = client,
client = client, chanId = chanId
chanId = chanId )
) }
}
normalF.map(_ => (chanId, assertion)) normalF.map(_ => (chanId, assertion))
} }
} }
executeWithClientOtherClient(getIsConfirmed) executeWithClientOtherClient(getIsConfirmed)
} }
val isClosedF = { val isClosedF = {
val getIsClosed = { val getIsClosed = { (client: EclairRpcClient, _: EclairRpcClient) =>
(client: EclairRpcClient, _: EclairRpcClient) => isConfirmedF.flatMap { isConfirmedF.flatMap {
case (chanId, assertion) => case (chanId, assertion) =>
val closedF = changeAddrF.flatMap { addr => val closedF = changeAddrF.flatMap { addr =>
val closedF = client.close(chanId, addr.scriptPubKey) val closedF = client.close(chanId, addr.scriptPubKey)
@ -159,7 +162,6 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
} }
closedF.flatMap { _ => closedF.flatMap { _ =>
val chanF = client.channel(chanId) val chanF = client.channel(chanId)
chanF.map { chan => chanF.map { chan =>
assert(chan.state == ChannelState.CLOSING) assert(chan.state == ChannelState.CLOSING)
@ -177,7 +179,7 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
val amountF = val amountF =
bitcoindRpcClientF.flatMap { bitcoindRpcClient => bitcoindRpcClientF.flatMap { bitcoindRpcClient =>
bitcoindRpcClient.getReceivedByAddress(address = addr, bitcoindRpcClient.getReceivedByAddress(address = addr,
minConfirmations = 0) minConfirmations = 0)
} }
amountF.map(amt => assert(amt > CurrencyUnits.zero)) amountF.map(amt => assert(amt > CurrencyUnits.zero))
} }
@ -199,28 +201,23 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
executeWithClientOtherClient[EclairAuthCredentials](getAuthCredentials) executeWithClientOtherClient[EclairAuthCredentials](getAuthCredentials)
} }
val badCredentialsF = goodCredentialsF.map { good => val badCredentialsF = goodCredentialsF.map { good =>
EclairAuthCredentials("bad_password", EclairAuthCredentials("bad_password", good.bitcoinAuthOpt, good.port)
good.bitcoinAuthOpt,
good.port)
} }
val badInstanceF = badCredentialsF.flatMap { badCredentials => val badInstanceF = badCredentialsF.flatMap { badCredentials =>
val getBadInstance = (client: EclairRpcClient, _: EclairRpcClient) => { val getBadInstance = (client: EclairRpcClient, _: EclairRpcClient) => {
val instance = EclairInstance(network = client.instance.network, val instance = EclairInstance(network = client.instance.network,
uri = client.instance.uri, uri = client.instance.uri,
rpcUri = client.instance.rpcUri, rpcUri = client.instance.rpcUri,
authCredentials = badCredentials) authCredentials = badCredentials)
Future.successful(instance) Future.successful(instance)
} }
executeWithClientOtherClient(getBadInstance) executeWithClientOtherClient(getBadInstance)
} }
val badClientF = badInstanceF.map(new EclairRpcClient(_)) val badClientF = badInstanceF.map(new EclairRpcClient(_))
badClientF.flatMap { badClient => badClientF.flatMap { badClient =>
@ -228,13 +225,11 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
} }
} }
it should "be able to list an existing peer and isConnected must be true" in { it should "be able to list an existing peer and isConnected must be true" in {
//test assumes that a connection to a peer was made in `beforeAll` //test assumes that a connection to a peer was made in `beforeAll`
val otherClientNodeIdF = { val otherClientNodeIdF = {
val getOtherClientNodeId = { val getOtherClientNodeId = {
(_:EclairRpcClient, otherClient: EclairRpcClient) => (_: EclairRpcClient, otherClient: EclairRpcClient) =>
otherClient.getInfo.map(_.nodeId) otherClient.getInfo.map(_.nodeId)
} }
@ -247,26 +242,28 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
it should "ble able to pay to a hash" in { it should "ble able to pay to a hash" in {
val amt = MilliSatoshis(50) val amt = MilliSatoshis(50)
val getPayment = { val getPayment = {
(client: EclairRpcClient,otherClient: EclairRpcClient) => { (client: EclairRpcClient, otherClient: EclairRpcClient) =>
for { {
channelId <- openAndConfirmChannel(clientF, otherClientF) for {
otherClientNodeId <- otherClient.getInfo.map(_.nodeId) channelId <- openAndConfirmChannel(clientF, otherClientF)
channels <- client.channels(otherClientNodeId) otherClientNodeId <- otherClient.getInfo.map(_.nodeId)
// without this we've been getting "route not found" channels <- client.channels(otherClientNodeId)
// probably an async issue, this is more elegant than Thread.sleep // without this we've been getting "route not found"
_ = assert(channels.exists(_.state == ChannelState.NORMAL), "Nodes did not have open channel!") // probably an async issue, this is more elegant than Thread.sleep
invoice <- otherClient.receive(amt.toLnCurrencyUnit) _ = assert(channels.exists(_.state == ChannelState.NORMAL),
payment <- client.send(amt.toLnCurrencyUnit, "Nodes did not have open channel!")
invoice.lnTags.paymentHash.hash, invoice <- otherClient.receive(amt.toLnCurrencyUnit)
otherClientNodeId) payment <- client.send(amt.toLnCurrencyUnit,
_ <- client.close(channelId) invoice.lnTags.paymentHash.hash,
_ <- bitcoindRpcClientF.flatMap(_.generate(6)) otherClientNodeId)
} yield { _ <- client.close(channelId)
assert(payment.isInstanceOf[PaymentSucceeded]) _ <- bitcoindRpcClientF.flatMap(_.generate(6))
val succeeded = payment.asInstanceOf[PaymentSucceeded] } yield {
assert(succeeded.amountMsat == amt) assert(payment.isInstanceOf[PaymentSucceeded])
val succeeded = payment.asInstanceOf[PaymentSucceeded]
assert(succeeded.amountMsat == amt)
}
} }
}
} }
executeWithClientOtherClient(getPayment) executeWithClientOtherClient(getPayment)
@ -277,19 +274,20 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
val amt = MilliSatoshis(50) val amt = MilliSatoshis(50)
val getPaymentWithAmount = { val getPaymentWithAmount = {
(client: EclairRpcClient, otherClient: EclairRpcClient) => { (client: EclairRpcClient, otherClient: EclairRpcClient) =>
for { {
channelId <- openAndConfirmChannel(clientF, otherClientF) for {
invoice <- otherClient.receive(amt.toLnCurrencyUnit) channelId <- openAndConfirmChannel(clientF, otherClientF)
payment <- client.send(invoice) invoice <- otherClient.receive(amt.toLnCurrencyUnit)
_ <- client.close(channelId) payment <- client.send(invoice)
_ <- bitcoindRpcClientF.flatMap(_.generate(6)) _ <- client.close(channelId)
} yield { _ <- bitcoindRpcClientF.flatMap(_.generate(6))
assert(payment.isInstanceOf[PaymentSucceeded]) } yield {
val succeeded = payment.asInstanceOf[PaymentSucceeded] assert(payment.isInstanceOf[PaymentSucceeded])
assert(succeeded.amountMsat == amt) val succeeded = payment.asInstanceOf[PaymentSucceeded]
assert(succeeded.amountMsat == amt)
}
} }
}
} }
executeWithClientOtherClient(getPaymentWithAmount) executeWithClientOtherClient(getPaymentWithAmount)
@ -298,19 +296,20 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
it should "be able to generate an invoice without amount and pay it" in { it should "be able to generate an invoice without amount and pay it" in {
val amt = MilliSatoshis(50) val amt = MilliSatoshis(50)
val getPaymentNoAmount = { val getPaymentNoAmount = {
(client: EclairRpcClient, otherClient: EclairRpcClient) => { (client: EclairRpcClient, otherClient: EclairRpcClient) =>
for { {
channelId <- openAndConfirmChannel(clientF, otherClientF) for {
invoice <- otherClient.receive("no amount") channelId <- openAndConfirmChannel(clientF, otherClientF)
payment <- client.send(invoice, amt.toLnCurrencyUnit) invoice <- otherClient.receive("no amount")
_ <- client.close(channelId) payment <- client.send(invoice, amt.toLnCurrencyUnit)
_ <- bitcoindRpcClientF.flatMap(_.generate(6)) _ <- client.close(channelId)
} yield { _ <- bitcoindRpcClientF.flatMap(_.generate(6))
assert(payment.isInstanceOf[PaymentSucceeded]) } yield {
val succeeded = payment.asInstanceOf[PaymentSucceeded] assert(payment.isInstanceOf[PaymentSucceeded])
assert(succeeded.amountMsat == amt) val succeeded = payment.asInstanceOf[PaymentSucceeded]
assert(succeeded.amountMsat == amt)
}
} }
}
} }
executeWithClientOtherClient(getPaymentNoAmount) executeWithClientOtherClient(getPaymentNoAmount)
@ -321,9 +320,10 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
val description = "bitcoin-s test case" val description = "bitcoin-s test case"
val expiry = (System.currentTimeMillis() / 1000) val expiry = (System.currentTimeMillis() / 1000)
val invoiceF = clientF.flatMap(_.receive(description = description, val invoiceF = clientF.flatMap(
amountMsat = amt, _.receive(description = description,
expirySeconds = expiry)) amountMsat = amt,
expirySeconds = expiry))
val assert0: Future[Assertion] = { val assert0: Future[Assertion] = {
invoiceF.map { i => invoiceF.map { i =>
@ -334,9 +334,10 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
} }
val amt1 = NanoBitcoins.one val amt1 = NanoBitcoins.one
val invoice1F = clientF.flatMap(_.receive(description = description, val invoice1F = clientF.flatMap(
amountMsat = amt1, _.receive(description = description,
expirySeconds = expiry)) amountMsat = amt1,
expirySeconds = expiry))
val assert1 = { val assert1 = {
invoice1F.map { i => invoice1F.map { i =>
@ -347,9 +348,10 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
} }
val amt2 = MicroBitcoins.one val amt2 = MicroBitcoins.one
val invoice2F = clientF.flatMap(_.receive(description = description, val invoice2F = clientF.flatMap(
amountMsat = amt2, _.receive(description = description,
expirySeconds = expiry)) amountMsat = amt2,
expirySeconds = expiry))
val assert2 = { val assert2 = {
invoice2F.map { i => invoice2F.map { i =>
@ -362,9 +364,10 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
val amt3 = MilliBitcoins.one val amt3 = MilliBitcoins.one
val invoice3F = clientF.flatMap(_.receive(description = description, val invoice3F = clientF.flatMap(
amountMsat = amt3, _.receive(description = description,
expirySeconds = expiry)) amountMsat = amt3,
expirySeconds = expiry))
val assert3 = { val assert3 = {
invoice3F.map { i => invoice3F.map { i =>
@ -385,9 +388,10 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
val description = "bitcoin-s test case" val description = "bitcoin-s test case"
val expiry = (System.currentTimeMillis() / 1000) val expiry = (System.currentTimeMillis() / 1000)
val invoiceF = clientF.flatMap(_.receive(description = description, val invoiceF = clientF.flatMap(
amountMsat = amt, _.receive(description = description,
expirySeconds = expiry)) amountMsat = amt,
expirySeconds = expiry))
val paymentRequestF: Future[PaymentRequest] = invoiceF.flatMap { i => val paymentRequestF: Future[PaymentRequest] = invoiceF.flatMap { i =>
clientF.flatMap(_.checkInvoice(i)) clientF.flatMap(_.checkInvoice(i))
@ -404,7 +408,8 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
val paymentAmount = NanoBitcoins(100000) val paymentAmount = NanoBitcoins(100000)
val invoiceF = val invoiceF =
openChannelIdF.flatMap(_ => otherClientF.flatMap(_.receive(paymentAmount))) openChannelIdF.flatMap(_ =>
otherClientF.flatMap(_.receive(paymentAmount)))
val paymentF = invoiceF.flatMap(i => clientF.flatMap(_.send(i))) val paymentF = invoiceF.flatMap(i => clientF.flatMap(_.send(i)))
@ -451,14 +456,17 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
val paymentAmount = NanoBitcoins(100000) val paymentAmount = NanoBitcoins(100000)
val invoiceF = val invoiceF =
openChannelIdF.flatMap(_ => otherClientF.flatMap(_.receive(paymentAmount))) openChannelIdF.flatMap(_ =>
otherClientF.flatMap(_.receive(paymentAmount)))
val isPaid1F = invoiceF.flatMap(i => otherClientF.flatMap(_.checkPayment(Left(i)))) val isPaid1F =
invoiceF.flatMap(i => otherClientF.flatMap(_.checkPayment(Left(i))))
val isNotPaidAssertF = isPaid1F.map(isPaid => assert(!isPaid)) val isNotPaidAssertF = isPaid1F.map(isPaid => assert(!isPaid))
//send the payment now //send the payment now
val paidF: Future[PaymentResult] = invoiceF.flatMap(i => clientF.flatMap(_.send(i))) val paidF: Future[PaymentResult] =
invoiceF.flatMap(i => clientF.flatMap(_.send(i)))
val isPaid2F: Future[Boolean] = paidF.flatMap { p => val isPaid2F: Future[Boolean] = paidF.flatMap { p =>
val succeed = p.asInstanceOf[PaymentSucceeded] val succeed = p.asInstanceOf[PaymentSucceeded]
@ -481,9 +489,11 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
val paymentAmount = NanoBitcoins(100000) val paymentAmount = NanoBitcoins(100000)
val invoiceF = val invoiceF =
openChannelIdF.flatMap(_ => otherClientF.flatMap(_.receive(paymentAmount))) openChannelIdF.flatMap(_ =>
otherClientF.flatMap(_.receive(paymentAmount)))
//send the payment now //send the payment now
val paidF: Future[PaymentResult] = invoiceF.flatMap(i => clientF.flatMap(_.send(i))) val paidF: Future[PaymentResult] =
invoiceF.flatMap(i => clientF.flatMap(_.send(i)))
val isPaidF: Future[Boolean] = paidF.flatMap { p => val isPaidF: Future[Boolean] = paidF.flatMap { p =>
val succeed = p.asInstanceOf[PaymentSucceeded] val succeed = p.asInstanceOf[PaymentSucceeded]
@ -493,7 +503,8 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
val isPaidAssertF = isPaidF.map(isPaid => assert(isPaid)) val isPaidAssertF = isPaidF.map(isPaid => assert(isPaid))
isPaidAssertF.flatMap { isPaid => isPaidAssertF.flatMap { isPaid =>
val invoice2F = openChannelIdF.flatMap(_ => clientF.flatMap(_.receive(paymentAmount))) val invoice2F =
openChannelIdF.flatMap(_ => clientF.flatMap(_.receive(paymentAmount)))
//send the payment now //send the payment now
val paid2F: Future[PaymentResult] = val paid2F: Future[PaymentResult] =
invoice2F.flatMap((i => otherClientF.flatMap(_.send(i)))) invoice2F.flatMap((i => otherClientF.flatMap(_.send(i))))
@ -520,7 +531,8 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
for { for {
(channel, oldFee) <- channelAndFeeF (channel, oldFee) <- channelAndFeeF
_ <- clientF.flatMap(_.updateRelayFee(channel, MilliSatoshis(oldFee.toLong * 2), 1)) _ <- clientF.flatMap(
_.updateRelayFee(channel, MilliSatoshis(oldFee.toLong * 2), 1))
newFeeOpt <- clientF.flatMap(_.channel(channel).map(_.feeBaseMsat)) newFeeOpt <- clientF.flatMap(_.channel(channel).map(_.feeBaseMsat))
} yield { } yield {
assert(newFeeOpt.isDefined) assert(newFeeOpt.isDefined)
@ -540,7 +552,8 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
it should "get a route to a node ID" in { it should "get a route to a node ID" in {
val hasRoute = () => { val hasRoute = () => {
fourthClientF.flatMap(_.getInfo) fourthClientF
.flatMap(_.getInfo)
.flatMap(info => firstClientF.flatMap(_.findRoute(info.nodeId))) .flatMap(info => firstClientF.flatMap(_.findRoute(info.nodeId)))
.map(route => route.length == 4) .map(route => route.length == 4)
.recover { .recover {
@ -559,14 +572,15 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
it should "get a route to an invoice" in { it should "get a route to an invoice" in {
val hasRoute = () => { val hasRoute = () => {
fourthClientF.flatMap { fourthClient => fourthClientF.flatMap { fourthClient =>
fourthClient.receive("foo") fourthClient
.receive("foo")
.flatMap { invoice => .flatMap { invoice =>
firstClientF.flatMap(_.findRoute(invoice)) firstClientF.flatMap(_.findRoute(invoice))
} }
.map(route => route.length == 4) .map(route => route.length == 4)
.recover { .recover {
case err: RuntimeException case err: RuntimeException
if err.getMessage.contains("route not found") => if err.getMessage.contains("route not found") =>
false false
} }
} }
@ -581,14 +595,21 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
it should "send some payments and get the audit info" in { it should "send some payments and get the audit info" in {
for { for {
invoice <- fourthClientF.flatMap(_.receive(MilliSatoshis(50000).toLnCurrencyUnit)) invoice <- fourthClientF.flatMap(
_ <- firstClientF.flatMap(_.send(invoice) _.receive(MilliSatoshis(50000).toLnCurrencyUnit))
.map(payment => assert(payment.isInstanceOf[PaymentSucceeded]))) _ <- firstClientF.flatMap(
_.send(invoice)
.map(payment => assert(payment.isInstanceOf[PaymentSucceeded])))
received <- fourthClientF.flatMap(_.audit()) received <- fourthClientF
.flatMap(_.audit())
.map(_.received) // check for received payments .map(_.received) // check for received payments
relayed <- secondClientF.flatMap(_.audit()).map(_.relayed) // check for relayed payments relayed <- secondClientF
sent <- firstClientF.flatMap(_.audit()).map(_.sent) // check for sent payments .flatMap(_.audit())
.map(_.relayed) // check for relayed payments
sent <- firstClientF
.flatMap(_.audit())
.map(_.sent) // check for sent payments
} yield { } yield {
assert(received.nonEmpty) assert(received.nonEmpty)
assert(relayed.nonEmpty) assert(relayed.nonEmpty)
@ -601,57 +622,66 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
// to them // to them
it should "get all channel updates for a given node ID" in { it should "get all channel updates for a given node ID" in {
val freshClients1F = bitcoindRpcClientF.flatMap { bitcoindRpcClient => val freshClients1F = bitcoindRpcClientF.flatMap { bitcoindRpcClient =>
EclairRpcTestUtil.createNodePair(Some(bitcoindRpcClient))
}
val freshClients2F = bitcoindRpcClientF.flatMap { bitcoindRpcClient =>
EclairRpcTestUtil.createNodePair(Some(bitcoindRpcClient)) EclairRpcTestUtil.createNodePair(Some(bitcoindRpcClient))
} }
val freshClients2F = bitcoindRpcClientF.flatMap { _ =>
thirdClientF.flatMap(t => fourthClientF.map(f => (t, f)))
}
val connectedClientsF: Future[EclairNodes4] = { val connectedClientsF: Future[EclairNodes4] = {
freshClients1F.flatMap { case (freshClient1, freshClient2) => freshClients1F.flatMap {
freshClients2F.flatMap { case (freshClient3,freshClient4) => case (freshClient1, freshClient2) =>
freshClients2F.flatMap {
case (freshClient3, freshClient4) =>
clients ++= List(freshClient1,
freshClient2,
freshClient3,
freshClient4)
clients ++= List(freshClient1, val connect1And3 =
freshClient2, EclairRpcTestUtil.connectLNNodes(freshClient1, freshClient3)
freshClient3, val connect1And4 = connect1And3.flatMap(_ =>
freshClient4) EclairRpcTestUtil.connectLNNodes(freshClient1, freshClient4))
connect1And3.flatMap { _ =>
val connect1And3 = EclairRpcTestUtil.connectLNNodes(freshClient1, freshClient3) connect1And4.map { _ =>
val connect1And4 = EclairRpcTestUtil.connectLNNodes(freshClient1, freshClient4) EclairNodes4(freshClient1,
connect1And3.flatMap { _ => freshClient2,
connect1And4.map { _ => freshClient3,
EclairNodes4(freshClient1, freshClient2, freshClient3, freshClient4) freshClient4)
} }
}
} }
}
} }
} }
def openChannel(c1: EclairRpcClient, c2: EclairRpcClient): Future[FundedChannelId] = { def openChannel(
c1: EclairRpcClient,
c2: EclairRpcClient): Future[FundedChannelId] = {
EclairRpcTestUtil EclairRpcTestUtil
.openChannel(c1, c2, Satoshis(Int64(500000)), MilliSatoshis(500000)) .openChannel(c1, c2, Satoshis(Int64(500000)), MilliSatoshis(500000))
} }
val openedChannelsF: Future[(ChannelId, ChannelId)] = { val openedChannelsF: Future[(ChannelId, ChannelId)] = {
connectedClientsF.flatMap { case nodes4: EclairNodes4 => connectedClientsF.flatMap {
val chan1F = openChannel(nodes4.c1, nodes4.c2) case nodes4: EclairNodes4 =>
val chan2F = openChannel(nodes4.c3, nodes4.c4) val chan1F = openChannel(nodes4.c1, nodes4.c2)
chan1F.flatMap(chanId1 => chan2F.map(chanId2 => (chanId1,chanId2))) val chan2F = openChannel(nodes4.c3, nodes4.c4)
chan1F.flatMap(chanId1 => chan2F.map(chanId2 => (chanId1, chanId2)))
} }
} }
val gen10F = openedChannelsF.flatMap(_ => bitcoindRpcClientF.flatMap(_.generate(10))) val gen10F =
openedChannelsF.flatMap(_ => bitcoindRpcClientF.flatMap(_.generate(10)))
val nodesReadyForPayments = gen10F.flatMap(_ => connectedClientsF) val nodesReadyForPayments = gen10F.flatMap(_ => connectedClientsF)
val sendPaymentsF = nodesReadyForPayments.flatMap { n4 => val sendPaymentsF = nodesReadyForPayments.flatMap { n4 =>
val p1F =
EclairRpcTestUtil.sendPayments(c1 = n4.c1, c2 = n4.c2, numPayments = 2)
val p2F =
EclairRpcTestUtil.sendPayments(c1 = n4.c3, c2 = n4.c4, numPayments = 2)
val p1F = EclairRpcTestUtil.sendPayments(n4.c1, n4.c2)
val p2F = EclairRpcTestUtil.sendPayments(n4.c3, n4.c4)
p1F.flatMap(_ => p2F) p1F.flatMap(_ => p2F)
} }
@ -677,10 +707,9 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
val client2F = connectedClientsF.map(_.c2) val client2F = connectedClientsF.map(_.c2)
sendPaymentsF.flatMap { _ => sendPaymentsF.flatMap { _ =>
executeSpecificClients(clientF = client1F, executeSpecificClients(clientF = client1F,
otherClientF = client2F, otherClientF = client2F,
test = getChannelUpdates) test = getChannelUpdates)
} }
} }
@ -692,29 +721,27 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
//about the channels for future fee calculations //about the channels for future fee calculations
val sentPaymentF = secondClientF.flatMap { c1 => val sentPaymentF = secondClientF.flatMap { c1 =>
thirdClientF.flatMap { c2 => thirdClientF.flatMap { c2 =>
EclairRpcTestUtil.sendPayments(c1,c2) EclairRpcTestUtil.sendPayments(c1, c2)
} }
} }
val gossipFromPeerWithNoChannel = { val gossipFromPeerWithNoChannel = {
(client: EclairRpcClient, nonPeer: EclairRpcClient) => (client: EclairRpcClient, nonPeer: EclairRpcClient) =>
sentPaymentF.flatMap { _ => sentPaymentF.flatMap { _ =>
val nodeIdF = client.getNodeURI.map(_.nodeId) val nodeIdF = client.getNodeURI.map(_.nodeId)
def ourUpdates = nodeIdF.flatMap(nonPeer.allUpdates(_)) def ourUpdates = nodeIdF.flatMap(nonPeer.allUpdates(_))
def allUpdates = nonPeer.allUpdates() def allUpdates = nonPeer.allUpdates()
def checkUpdates() : Future[Boolean] = { def checkUpdates(): Future[Boolean] = {
ourUpdates.flatMap( our => ourUpdates.flatMap(our =>
allUpdates.map { all => allUpdates.map { all =>
our != all our != all
} })
)
} }
val checkedUpatesF: Future[Unit] = AsyncUtil.retryUntilSatisfiedF( val checkedUpatesF: Future[Unit] =
checkUpdates, AsyncUtil.retryUntilSatisfiedF(checkUpdates,
duration = 5.seconds, duration = 5.seconds,
maxTries = 15) maxTries = 15)
val hasUpdateP = Promise[Assertion]() val hasUpdateP = Promise[Assertion]()
checkedUpatesF.onComplete { t => checkedUpatesF.onComplete { t =>
@ -795,7 +822,8 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
genF.flatMap { _ => genF.flatMap { _ =>
//wait until our peer has put the channel in the //wait until our peer has put the channel in the
//NORMAL state so we can route payments to them //NORMAL state so we can route payments to them
val normalF = client2F.flatMap(c2 => EclairRpcTestUtil.awaitUntilChannelNormal(c2, cid)) val normalF = client2F.flatMap(c2 =>
EclairRpcTestUtil.awaitUntilChannelNormal(c2, cid))
normalF.map(_ => cid) normalF.map(_ => cid)
@ -804,7 +832,7 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll {
} }
private def updateIsInChannels(channels: Seq[OpenChannelInfo])( private def updateIsInChannels(channels: Seq[OpenChannelInfo])(
update: ChannelUpdate): Boolean = { update: ChannelUpdate): Boolean = {
channels.exists(_.shortChannelId == update.shortChannelId) channels.exists(_.shortChannelId == update.shortChannelId)
} }

View file

@ -476,7 +476,7 @@ class EclairRpcClient(val instance: EclairInstance)(
val payloadF: Future[JsValue] = responseF.flatMap(getPayload) val payloadF: Future[JsValue] = responseF.flatMap(getPayload)
payloadF.map { payload => payloadF.map { payload =>
val validated: JsResult[T] = (payload \ resultKey).validate[T] val validated: JsResult[T] = (payload \ resultKey).validate[T]
val parsed: T = parseResult(validated, payload) val parsed: T = parseResult(validated, payload, command)
parsed parsed
} }
} }
@ -484,16 +484,19 @@ class EclairRpcClient(val instance: EclairInstance)(
case class RpcError(code: Int, message: String) case class RpcError(code: Int, message: String)
implicit val rpcErrorReads: Reads[RpcError] = Json.reads[RpcError] implicit val rpcErrorReads: Reads[RpcError] = Json.reads[RpcError]
private def parseResult[T](result: JsResult[T], json: JsValue): T = { private def parseResult[T](result: JsResult[T], json: JsValue, commandName: String): T = {
result match { result match {
case res: JsSuccess[T] => case res: JsSuccess[T] =>
res.value res.value
case res: JsError => case res: JsError =>
(json \ errorKey).validate[RpcError] match { (json \ errorKey).validate[RpcError] match {
case err: JsSuccess[RpcError] => case err: JsSuccess[RpcError] =>
logger.error(s"Error ${err.value.code}: ${err.value.message}") val datadirMsg = instance.authCredentials.datadir
throw new RuntimeException( .map(d => s"datadir=${d}")
s"Error ${err.value.code}: ${err.value.message}") .getOrElse("")
val errMsg = s"Error for command=${commandName} ${datadirMsg}, ${err.value.code}=${err.value.message}"
logger.error(errMsg)
throw new RuntimeException(errMsg)
case _: JsError => case _: JsError =>
logger.error(JsError.toJson(res).toString()) logger.error(JsError.toJson(res).toString())
throw new IllegalArgumentException( throw new IllegalArgumentException(

View file

@ -7,8 +7,13 @@ import akka.actor.ActorSystem
import com.typesafe.config.{Config, ConfigFactory} import com.typesafe.config.{Config, ConfigFactory}
import org.bitcoins.core.config.RegTest import org.bitcoins.core.config.RegTest
import org.bitcoins.core.currency.CurrencyUnit import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.protocol.ln.channel.{ChannelId, ChannelState, FundedChannelId} import org.bitcoins.core.protocol.ln.channel.{
ChannelId,
ChannelState,
FundedChannelId
}
import org.bitcoins.core.protocol.ln.currency.MilliSatoshis import org.bitcoins.core.protocol.ln.currency.MilliSatoshis
import org.bitcoins.core.protocol.ln.node.NodeId
import org.bitcoins.core.util.BitcoinSLogger import org.bitcoins.core.util.BitcoinSLogger
import org.bitcoins.eclair.rpc.client.EclairRpcClient import org.bitcoins.eclair.rpc.client.EclairRpcClient
import org.bitcoins.eclair.rpc.config.EclairInstance import org.bitcoins.eclair.rpc.config.EclairInstance
@ -21,6 +26,7 @@ import org.bitcoins.util.AsyncUtil
import scala.concurrent.duration.DurationInt import scala.concurrent.duration.DurationInt
import scala.concurrent.{ExecutionContext, Future} import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
/** /**
* @define nodeLinkDoc * @define nodeLinkDoc
@ -217,7 +223,8 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
}(system.dispatcher) }(system.dispatcher)
} }
AsyncUtil.retryUntilSatisfiedF(conditionF = () => isState(), duration = 1.seconds) AsyncUtil.retryUntilSatisfiedF(conditionF = () => isState(),
duration = 1.seconds)
} }
private def createNodeLink( private def createNodeLink(
@ -237,7 +244,7 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
internalBitcoindF.flatMap(b => createNodePair(Some(b))) internalBitcoindF.flatMap(b => createNodePair(Some(b)))
} }
val pair2: Future[(EclairRpcClient,EclairRpcClient)] = { val pair2: Future[(EclairRpcClient, EclairRpcClient)] = {
internalBitcoindF.flatMap(b => createNodePair(Some(b))) internalBitcoindF.flatMap(b => createNodePair(Some(b)))
} }
@ -251,32 +258,30 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
} }
val nodeVecF: Future[Vector[EclairRpcClient]] = { val nodeVecF: Future[Vector[EclairRpcClient]] = {
pair1.flatMap { case (first,second) => pair1.flatMap {
pair2.flatMap { case (first, second) =>
case (third,fourth) => pair2.flatMap {
case (third, fourth) =>
// we need to make sure the second and third nodes are connected
val connected = EclairRpcTestUtil.connectLNNodes(second, third)
connected.map { _ =>
Vector(first, second, third, fourth)
}
// we need to make sure the second and third nodes are connected }
val connected = EclairRpcTestUtil.connectLNNodes(second, third)
connected.map { _ =>
Vector(first,second,third,fourth)
}
}
} }
} }
val openChannelsFNested: Future[List[Future[FundedChannelId]]] = { val openChannelsFNested: Future[List[Future[FundedChannelId]]] = {
nodeVecF.map { nodeVec => nodeVecF.map { nodeVec =>
List(open(nodeVec.head, nodeVec(1)), List(open(nodeVec.head, nodeVec(1)),
open(nodeVec(1), nodeVec(2)), open(nodeVec(1), nodeVec(2)),
open(nodeVec(2), nodeVec(3))) open(nodeVec(2), nodeVec(3)))
} }
} }
val openChannelsF :Future[List[FundedChannelId]] = { val openChannelsF: Future[List[FundedChannelId]] = {
openChannelsFNested.flatMap(Future.sequence(_)) openChannelsFNested.flatMap(Future.sequence(_))
} }
@ -322,7 +327,8 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
* @return A 4-tuple of the created nodes' respective * @return A 4-tuple of the created nodes' respective
* [[org.bitcoins.eclair.rpc.client.EclairRpcClient EclairRpcClient]] * [[org.bitcoins.eclair.rpc.client.EclairRpcClient EclairRpcClient]]
*/ */
def createNodeLink()(implicit actorSystem: ActorSystem): Future[EclairNodes4] = { def createNodeLink()(
implicit actorSystem: ActorSystem): Future[EclairNodes4] = {
createNodeLink(None, DEFAULT_CHANNEL_MSAT_AMT) createNodeLink(None, DEFAULT_CHANNEL_MSAT_AMT)
} }
@ -343,7 +349,8 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
* respective [[org.bitcoins.eclair.rpc.client.EclairRpcClient EclairRpcClient]]s * respective [[org.bitcoins.eclair.rpc.client.EclairRpcClient EclairRpcClient]]s
*/ */
def createNodePair(bitcoindRpcClientOpt: Option[BitcoindRpcClient])( def createNodePair(bitcoindRpcClientOpt: Option[BitcoindRpcClient])(
implicit system: ActorSystem): Future[(EclairRpcClient, EclairRpcClient)] = { implicit system: ActorSystem): Future[
(EclairRpcClient, EclairRpcClient)] = {
import system.dispatcher import system.dispatcher
val bitcoindRpcClientF: Future[BitcoindRpcClient] = { val bitcoindRpcClientF: Future[BitcoindRpcClient] = {
@ -354,8 +361,10 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
} }
} }
val e1InstanceF = bitcoindRpcClientF.map(EclairRpcTestUtil.eclairInstance(_)) val e1InstanceF =
val e2InstanceF = bitcoindRpcClientF.map(EclairRpcTestUtil.eclairInstance(_)) bitcoindRpcClientF.map(EclairRpcTestUtil.eclairInstance(_))
val e2InstanceF =
bitcoindRpcClientF.map(EclairRpcTestUtil.eclairInstance(_))
val clientF = e1InstanceF.flatMap { e1 => val clientF = e1InstanceF.flatMap { e1 =>
val e = new EclairRpcClient(e1) val e = new EclairRpcClient(e1)
@ -372,14 +381,15 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
logger.debug(s"Both clients started") logger.debug(s"Both clients started")
val connectedLnF: Future[(EclairRpcClient, EclairRpcClient)] = clientF.flatMap { c1 => val connectedLnF: Future[(EclairRpcClient, EclairRpcClient)] =
otherClientF.flatMap { c2 => clientF.flatMap { c1 =>
val connectedF = connectLNNodes(c1, c2) otherClientF.flatMap { c2 =>
connectedF.map { _ => val connectedF = connectLNNodes(c1, c2)
(c1,c2) connectedF.map { _ =>
(c1, c2)
}
} }
} }
}
connectedLnF connectedLnF
} }
@ -389,13 +399,13 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
system: ActorSystem): Future[Unit] = { system: ActorSystem): Future[Unit] = {
implicit val dispatcher = system.dispatcher implicit val dispatcher = system.dispatcher
val infoF = otherClient.getInfo val infoF = otherClient.getInfo
val nodeIdF = infoF.map(_.nodeId)
val connection: Future[String] = infoF.flatMap { info => val connection: Future[String] = infoF.flatMap { info =>
client.connect(info.nodeId, "localhost", info.port) client.connect(info.nodeId, "localhost", info.port)
} }
def isConnected(): Future[Boolean] = { def isConnected(): Future[Boolean] = {
val nodeIdF = infoF.map(_.nodeId)
nodeIdF.flatMap { nodeId => nodeIdF.flatMap { nodeId =>
connection.flatMap { _ => connection.flatMap { _ =>
val connected: Future[Boolean] = client.isConnected(nodeId) val connected: Future[Boolean] = client.isConnected(nodeId)
@ -405,9 +415,9 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
} }
logger.debug(s"Awaiting connection between clients") logger.debug(s"Awaiting connection between clients")
val connected = RpcUtil.retryUntilSatisfiedF( val connected = RpcUtil.retryUntilSatisfiedF(conditionF =
conditionF = () => isConnected(), () => isConnected(),
duration = 1.second) duration = 1.second)
connected.map(_ => logger.debug(s"Successfully connected two clients")) connected.map(_ => logger.debug(s"Successfully connected two clients"))
@ -424,8 +434,8 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
def sendPayments( def sendPayments(
c1: EclairRpcClient, c1: EclairRpcClient,
c2: EclairRpcClient, c2: EclairRpcClient,
numPayments: Int = 10)( numPayments: Int = 5)(
implicit ec: ExecutionContext): Future[Seq[PaymentResult]] = { implicit ec: ExecutionContext): Future[Vector[PaymentResult]] = {
val payments = (1 to numPayments) val payments = (1 to numPayments)
.map(MilliSatoshis(_)) .map(MilliSatoshis(_))
.map( .map(
@ -434,7 +444,22 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
.flatMap(invoice => c2.send(invoice, sats.toLnCurrencyUnit)) .flatMap(invoice => c2.send(invoice, sats.toLnCurrencyUnit))
) )
Future.sequence(payments) val resultF = Future.sequence(payments).map(_.toVector)
resultF.onComplete {
case Success(_) =>
case Failure(_) =>
val nodeId1F = c1.nodeId()
val nodeId2F = c2.nodeId()
nodeId1F.flatMap { nid1 =>
nodeId2F.map { nid2 =>
logger.error(
s"Failed to send payments from ${nid1.hex} -> ${nid2.hex}")
}
}
}
resultF
} }
private val DEFAULT_CHANNEL_MSAT_AMT = MilliSatoshis(500000000L) private val DEFAULT_CHANNEL_MSAT_AMT = MilliSatoshis(500000000L)
@ -450,23 +475,30 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
val bitcoindRpcClient = getBitcoindRpc(n1) val bitcoindRpcClient = getBitcoindRpc(n1)
implicit val ec = system.dispatcher implicit val ec = system.dispatcher
val fundedChannelIdF: Future[FundedChannelId] = {
n2.nodeId.flatMap { nodeId =>
n1.getInfo.map { info =>
logger.debug(
s"Opening a channel from ${info.nodeId} -> ${nodeId} with amount ${amt}")
} val n1NodeIdF = n1.nodeId()
n1.open(nodeId = nodeId, val n2NodeIdF = n2.nodeId()
fundingSatoshis = amt,
pushMsat = Some(pushMSat), val nodeIdsF: Future[(NodeId, NodeId)] = {
feerateSatPerByte = None, n1NodeIdF.flatMap(n1 => n2NodeIdF.map(n2 => (n1, n2)))
channelFlags = None) }
val fundedChannelIdF: Future[FundedChannelId] = {
nodeIdsF.flatMap {
case (nodeId1, nodeId2) =>
logger.debug(
s"Opening a channel from ${nodeId1} -> ${nodeId2} with amount ${amt}")
n1.open(nodeId = nodeId2,
fundingSatoshis = amt,
pushMsat = Some(pushMSat),
feerateSatPerByte = None,
channelFlags = None)
} }
} }
val gen = fundedChannelIdF.flatMap(_ => bitcoindRpcClient.generate(6)) val gen = fundedChannelIdF.flatMap(_ => bitcoindRpcClient.generate(6))
val opened = { val openedF = {
gen.flatMap { _ => gen.flatMap { _ =>
fundedChannelIdF.flatMap { fcid => fundedChannelIdF.flatMap { fcid =>
val chanOpenF = awaitChannelOpened(n1, fcid) val chanOpenF = awaitChannelOpened(n1, fcid)
@ -474,12 +506,17 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
} }
} }
} }
opened.map {
openedF.flatMap {
case _ => case _ =>
logger.debug( nodeIdsF.map {
s"Channel successfully opened ${n1.getNodeURI} -> ${n2.getNodeURI} with amount $amt") case (nodeId1, nodeId2) =>
logger.debug(
s"Channel successfully opened ${nodeId1} -> ${nodeId2} with amount $amt")
}
} }
opened
openedF
} }
def awaitChannelOpened(client1: EclairRpcClient, chanId: ChannelId)( def awaitChannelOpened(client1: EclairRpcClient, chanId: ChannelId)(