diff --git a/README.md b/README.md
index 17d28b3e2..ae2e5fbad 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
[](LICENSE)
[](https://gitter.im/ACINQ/eclair)
-**Eclair** (french for Lightning) is a scala implementation of the Lightning Network. It can run with or without a GUI, and a JSON-RPC API is also available.
+**Eclair** (French for Lightning) is a Scala implementation of the Lightning Network. It can run with or without a GUI, and a JSON-RPC API is also available.
This software follows the [Lightning Network Specifications (BOLTs)](https://github.com/lightningnetwork/lightning-rfc). Other implementations include [c-lightning](https://github.com/ElementsProject/lightning) and [lnd](https://github.com/LightningNetwork/lnd).
@@ -101,7 +101,8 @@ name | description
eclair.api.password | API password (BASIC) | "" (must be set if the API is enabled)
eclair.bitcoind.rpcuser | Bitcoin Core RPC user | foo
eclair.bitcoind.rpcpassword | Bitcoin Core RPC password | bar
- eclair.bitcoind.zmq | Bitcoin Core ZMQ address | "tcp://127.0.0.1:29000"
+ eclair.bitcoind.zmqblock | Bitcoin Core ZMQ block address | "tcp://127.0.0.1:29000"
+ eclair.bitcoind.zmqtx | Bitcoin Core ZMQ tx address | "tcp://127.0.0.1:29000"
eclair.gui.unit | Unit in which amounts are displayed (possible values: msat, sat, mbtc, btc) | btc
Quotes are not required unless the value contains special characters. Full syntax guide [here](https://github.com/lightbend/config/blob/master/HOCON.md).
@@ -208,7 +209,7 @@ addresstype=p2sh-segwit
deprecatedrpc=signrawtransaction
```
-You may also want to take advantage of the new configuration sections in `bitcoin.conf` to manage parameters that are network speficic, so you can reasliy run your bitcoin node on both mainnet and testnet. For example you could use:
+You may also want to take advantage of the new configuration sections in `bitcoin.conf` to manage parameters that are network specific, so you can easily run your bitcoin node on both mainnet and testnet. For example you could use:
```
server=1
diff --git a/eclair-core/eclair-cli b/eclair-core/eclair-cli
index 013346957..66e9cd358 100755
--- a/eclair-core/eclair-cli
+++ b/eclair-core/eclair-cli
@@ -2,6 +2,9 @@
# Check if jq is installed. If not, display instructions and abort program
command -v jq >/dev/null 2>&1 || { echo -e "This tool requires jq.\nFor installation instructions, visit https://stedolan.github.io/jq/download/.\n\nAborting..."; exit 1; }
+# curl installed? If not, give a hint
+command -v curl >/dev/null 2>&1 || { echo -e "This tool requires curl.\n\nAborting..."; exit 1; }
+
FULL_OUTPUT='false'
URL='http://localhost:8080'
PASSWORD=''
diff --git a/eclair-core/pom.xml b/eclair-core/pom.xml
index c294e7e73..fdcace6ca 100644
--- a/eclair-core/pom.xml
+++ b/eclair-core/pom.xml
@@ -126,17 +126,22 @@
akka-slf4j_${scala.version.short}
${akka.version}
-
+
- com.ning
- async-http-client
- 1.9.40
+ com.softwaremill.sttp
+ okhttp-backend_${scala.version.short}
+ ${sttp.version}
org.json4s
json4s-jackson_${scala.version.short}
- 3.5.3
+ 3.6.0
+
+
+ com.softwaremill.sttp
+ json4s_${scala.version.short}
+ ${sttp.version}
com.lihaoyi
diff --git a/eclair-core/src/main/resources/reference.conf b/eclair-core/src/main/resources/reference.conf
index 197a4e012..f42e27d8d 100644
--- a/eclair-core/src/main/resources/reference.conf
+++ b/eclair-core/src/main/resources/reference.conf
@@ -22,7 +22,8 @@ eclair {
rpcport = 18332
rpcuser = "foo"
rpcpassword = "bar"
- zmq = "tcp://127.0.0.1:29000"
+ zmqblock = "tcp://127.0.0.1:29000"
+ zmqtx = "tcp://127.0.0.1:29000"
}
default-feerates { // those are in satoshis per kilobyte
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/HttpHelper.scala b/eclair-core/src/main/scala/fr/acinq/eclair/HttpHelper.scala
deleted file mode 100644
index dcb8b4c46..000000000
--- a/eclair-core/src/main/scala/fr/acinq/eclair/HttpHelper.scala
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2018 ACINQ SAS
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package fr.acinq.eclair
-
-import com.ning.http.client.{AsyncCompletionHandler, AsyncHttpClient, AsyncHttpClientConfig, Response}
-import grizzled.slf4j.Logging
-import org.json4s.DefaultFormats
-import org.json4s.JsonAST.{JNothing, JValue}
-import org.json4s.jackson.JsonMethods.parse
-
-import scala.concurrent.{ExecutionContext, Future, Promise}
-import scala.util.{Failure, Success, Try}
-
-object HttpHelper extends Logging {
-
- val client = new AsyncHttpClient(new AsyncHttpClientConfig.Builder().setAcceptAnyCertificate(true).build())
-
- implicit val formats = DefaultFormats
-
- def get(url: String)(implicit ec: ExecutionContext): Future[JValue] = {
- val promise = Promise[JValue]
- client
- .prepareGet(url)
- .execute(new AsyncCompletionHandler[Unit] {
- override def onCompleted(response: Response): Unit = {
- Try(parse(response.getResponseBody)) match {
- case Success(json) => promise.success(json)
- case Failure(t) => promise.success(JNothing)
- }
- }
- })
- val f = promise.future
- f onFailure {
- case t: Throwable => logger.error(s"GET $url failed: ", t)
- }
- f
- }
-
-}
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala
index eb322586e..ab50ac6e8 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala
@@ -20,6 +20,7 @@ import java.io.File
import akka.actor.{ActorRef, ActorSystem, Props, SupervisorStrategy}
import akka.util.Timeout
+import com.softwaremill.sttp.okhttp.OkHttpFutureBackend
import com.typesafe.config.{Config, ConfigFactory}
import fr.acinq.bitcoin.{BinaryData, Block}
import fr.acinq.eclair.NodeParams.ELECTRUM
@@ -38,12 +39,12 @@ import scala.concurrent.{ExecutionContext, Future, Promise}
/**
- * Setup eclair from a datadir.
+ * Setup eclair from a data directory.
*
* Created by PM on 25/01/2016.
*
- * @param datadir directory where eclair-core will write/read its data
- * @param overrideDefaults
+ * @param datadir directory where eclair-core will write/read its data.
+ * @param overrideDefaults use this parameter to programmatically override the node configuration .
* @param seed_opt optional seed, if set eclair will use it instead of generating one and won't create a seed.dat file.
*/
class Setup(datadir: File,
@@ -64,9 +65,8 @@ class Setup(datadir: File,
logger.info(s"nodeid=${nodeParams.nodeId} alias=${nodeParams.alias}")
logger.info(s"using chain=$chain chainHash=${nodeParams.chainHash}")
- implicit val timeout = Timeout(30 seconds)
- implicit val formats = org.json4s.DefaultFormats
implicit val ec = ExecutionContext.Implicits.global
+ implicit val sttpBackend = OkHttpFutureBackend()
def bootstrap: Future[Kit] =
for {
@@ -121,6 +121,7 @@ class Setup(datadir: File,
wallet = bitcoin match {
case Electrum(electrumClient) =>
val electrumWallet = system.actorOf(ElectrumWallet.props(seed, electrumClient, ElectrumWallet.WalletParameters(nodeParams.chainHash)), "electrum-wallet")
+ implicit val timeout = Timeout(30 seconds)
new ElectrumEclairWallet(electrumWallet, nodeParams.chainHash)
case _ => ???
}
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreWallet.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreWallet.scala
index 61c34693e..d608e92c8 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreWallet.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreWallet.scala
@@ -16,7 +16,6 @@
package fr.acinq.eclair.blockchain.bitcoind
-import akka.actor.ActorSystem
import fr.acinq.bitcoin._
import fr.acinq.eclair._
import fr.acinq.eclair.blockchain._
@@ -30,11 +29,10 @@ import scala.concurrent.{ExecutionContext, Future}
/**
* Created by PM on 06/07/2017.
*/
-class BitcoinCoreWallet(rpcClient: BitcoinJsonRPCClient)(implicit system: ActorSystem, ec: ExecutionContext) extends EclairWallet with Logging {
+class BitcoinCoreWallet(rpcClient: BitcoinJsonRPCClient)(implicit ec: ExecutionContext) extends EclairWallet with Logging {
import BitcoinCoreWallet._
-
def fundTransaction(hex: String, lockUnspents: Boolean, feeRatePerKw: Long): Future[FundTransactionResponse] = {
val feeRatePerKB = BigDecimal(feerateKw2KB(feeRatePerKw))
rpcClient.invoke("fundrawtransaction", hex, Options(lockUnspents, feeRatePerKB.bigDecimal.scaleByPowerOfTen(-8))).map(json => {
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/BasicBitcoinJsonRPCClient.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/BasicBitcoinJsonRPCClient.scala
index d2690bfc0..5be0d2a82 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/BasicBitcoinJsonRPCClient.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/rpc/BasicBitcoinJsonRPCClient.scala
@@ -16,68 +16,36 @@
package fr.acinq.eclair.blockchain.bitcoind.rpc
-import akka.actor.ActorSystem
-import com.ning.http.client._
+import com.softwaremill.sttp._
+import com.softwaremill.sttp.json4s._
import org.json4s.DefaultFormats
-import org.json4s.JsonAST.{JInt, JNull, JString, JValue}
-import org.json4s.jackson.JsonMethods.parse
-import org.json4s.jackson.Serialization._
+import org.json4s.JsonAST.JValue
+import org.json4s.jackson.Serialization
-import scala.concurrent.ExecutionContext.Implicits.global
-import scala.concurrent.{ExecutionContext, Future, Promise}
+import scala.concurrent.{ExecutionContext, Future}
-class BasicBitcoinJsonRPCClient(config: AsyncHttpClientConfig, host: String, port: Int, ssl: Boolean)(implicit system: ActorSystem) extends BitcoinJsonRPCClient {
-
- def this(user: String, password: String, host: String = "127.0.0.1", port: Int = 8332, ssl: Boolean = false)(implicit system: ActorSystem) = this(
- new AsyncHttpClientConfig.Builder()
- .setRealm(new Realm.RealmBuilder().setPrincipal(user).setPassword(password).setUsePreemptiveAuth(true).setScheme(Realm.AuthScheme.BASIC).build)
- .build,
- host,
- port,
- ssl
- )
-
- val client: AsyncHttpClient = new AsyncHttpClient(config)
+class BasicBitcoinJsonRPCClient(user: String, password: String, host: String = "127.0.0.1", port: Int = 8332, ssl: Boolean = false)(implicit http: SttpBackend[Future, Nothing]) extends BitcoinJsonRPCClient {
+ val scheme = if (ssl) "https" else "http"
implicit val formats = DefaultFormats.withBigDecimal
+ implicit val serialization = Serialization
override def invoke(method: String, params: Any*)(implicit ec: ExecutionContext): Future[JValue] =
- invoke(JsonRPCRequest(method = method, params = params))
+ invoke(Seq(JsonRPCRequest(method = method, params = params))).map(l => jsonResponse2Exception(l.head).result)
def jsonResponse2Exception(jsonRPCResponse: JsonRPCResponse): JsonRPCResponse = jsonRPCResponse match {
case JsonRPCResponse(_, Some(error), _) => throw JsonRPCError(error)
case o => o
}
- def invoke(request: JsonRPCRequest)(implicit ec: ExecutionContext): Future[JValue] = {
- val promise = Promise[JValue]()
- client
- .preparePost((if (ssl) "https" else "http") + s"://$host:$port/")
- .addHeader("Content-Type", "application/json")
- .setBody(write(request))
- .execute(new AsyncCompletionHandler[Unit] {
- override def onCompleted(response: Response): Unit =
- try {
- val jvalue = parse(response.getResponseBody, useBigDecimalForDouble = true)
- val jerror = jvalue \ "error"
- val result = jvalue \ "result"
- if (jerror != JNull) {
- for {
- JInt(code) <- jerror \ "code"
- JString(message) <- jerror \ "message"
- } yield promise.failure(new JsonRPCError(Error(code.toInt, message)))
- } else {
- promise.success(result)
- }
- } catch {
- case t: Throwable => promise.failure(t)
- }
-
- override def onThrowable(t: Throwable): Unit = promise.failure(t)
- })
- promise.future
- }
-
- def invoke(request: Seq[(String, Seq[Any])])(implicit ec: ExecutionContext): Future[Seq[JValue]] = ???
+ def invoke(requests: Seq[JsonRPCRequest])(implicit ec: ExecutionContext): Future[Seq[JsonRPCResponse]] =
+ for {
+ res <- sttp
+ .post(uri"$scheme://$host:$port")
+ .body(requests)
+ .auth.basic(user, password)
+ .response(asJson[Seq[JsonRPCResponse]])
+ .send()
+ } yield res.unsafeBody
}
\ No newline at end of file
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/BitcoinCoreFeeProvider.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/BitcoinCoreFeeProvider.scala
index 5cc6e7ae7..b56c878df 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/BitcoinCoreFeeProvider.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/BitcoinCoreFeeProvider.scala
@@ -17,7 +17,7 @@
package fr.acinq.eclair.blockchain.fee
import fr.acinq.bitcoin._
-import fr.acinq.eclair.blockchain.bitcoind.rpc.{BitcoinJsonRPCClient, Error, JsonRPCError}
+import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinJsonRPCClient
import org.json4s.DefaultFormats
import org.json4s.JsonAST._
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/BitgoFeeProvider.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/BitgoFeeProvider.scala
index f98ca7acd..bf6815186 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/BitgoFeeProvider.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/BitgoFeeProvider.scala
@@ -16,26 +16,33 @@
package fr.acinq.eclair.blockchain.fee
-import akka.actor.ActorSystem
+import com.softwaremill.sttp._
+import com.softwaremill.sttp.json4s._
import fr.acinq.bitcoin.{BinaryData, Block}
-import fr.acinq.eclair.HttpHelper.get
+import org.json4s.DefaultFormats
import org.json4s.JsonAST.{JInt, JValue}
+import org.json4s.jackson.Serialization
import scala.concurrent.{ExecutionContext, Future}
-class BitgoFeeProvider(chainHash: BinaryData)(implicit system: ActorSystem, ec: ExecutionContext) extends FeeProvider {
+class BitgoFeeProvider(chainHash: BinaryData)(implicit http: SttpBackend[Future, Nothing], ec: ExecutionContext) extends FeeProvider {
import BitgoFeeProvider._
+ implicit val formats = DefaultFormats
+ implicit val serialization = Serialization
+
val uri = chainHash match {
- case Block.LivenetGenesisBlock.hash => "https://www.bitgo.com/api/v2/btc/tx/fee"
- case _ => "https://test.bitgo.com/api/v2/tbtc/tx/fee"
+ case Block.LivenetGenesisBlock.hash => uri"https://www.bitgo.com/api/v2/btc/tx/fee"
+ case _ => uri"https://test.bitgo.com/api/v2/tbtc/tx/fee"
}
override def getFeerates: Future[FeeratesPerKB] =
for {
- json <- get(uri)
- feeRanges = parseFeeRanges(json)
+ res <- sttp.get(uri)
+ .response(asJson[JValue])
+ .send()
+ feeRanges = parseFeeRanges(res.unsafeBody)
} yield extractFeerates(feeRanges)
}
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/EarnDotComFeeProvider.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/EarnDotComFeeProvider.scala
index c38f7dad4..2176ce481 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/EarnDotComFeeProvider.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/fee/EarnDotComFeeProvider.scala
@@ -16,23 +16,32 @@
package fr.acinq.eclair.blockchain.fee
-import akka.actor.ActorSystem
-import fr.acinq.eclair.HttpHelper.get
+import com.softwaremill.sttp._
+import com.softwaremill.sttp.json4s._
+import org.json4s.DefaultFormats
import org.json4s.JsonAST.{JArray, JInt, JValue}
+import org.json4s.jackson.Serialization
import scala.concurrent.{ExecutionContext, Future}
/**
* Created by PM on 16/11/2017.
*/
-class EarnDotComFeeProvider(implicit system: ActorSystem, ec: ExecutionContext) extends FeeProvider {
+class EarnDotComFeeProvider(implicit http: SttpBackend[Future, Nothing], ec: ExecutionContext) extends FeeProvider {
import EarnDotComFeeProvider._
+ implicit val formats = DefaultFormats
+ implicit val serialization = Serialization
+
+ val uri = uri"https://bitcoinfees.earn.com/api/v1/fees/list"
+
override def getFeerates: Future[FeeratesPerKB] =
for {
- json <- get("https://bitcoinfees.earn.com/api/v1/fees/list")
- feeRanges = parseFeeRanges(json)
+ json <- sttp.get(uri)
+ .response(asJson[JValue])
+ .send()
+ feeRanges = parseFeeRanges(json.unsafeBody)
} yield extractFeerates(feeRanges)
}
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelTypes.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelTypes.scala
index 2bbc8d139..d3ea2e2f8 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelTypes.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelTypes.scala
@@ -105,7 +105,7 @@ case class BITCOIN_PARENT_TX_CONFIRMED(childTx: Transaction) extends BitcoinEven
*/
sealed trait Command
-final case class CMD_ADD_HTLC(amountMsat: Long, paymentHash: BinaryData, expiry: Long, onion: BinaryData = Sphinx.LAST_PACKET.serialize, upstream_opt: Option[UpdateAddHtlc] = None, commit: Boolean = false, redirected: Boolean = false) extends Command
+final case class CMD_ADD_HTLC(amountMsat: Long, paymentHash: BinaryData, cltvExpiry: Long, onion: BinaryData = Sphinx.LAST_PACKET.serialize, upstream_opt: Option[UpdateAddHtlc] = None, commit: Boolean = false, redirected: Boolean = false) extends Command
final case class CMD_FULFILL_HTLC(id: Long, r: BinaryData, commit: Boolean = false) extends Command
final case class CMD_FAIL_HTLC(id: Long, reason: Either[BinaryData, FailureMessage], commit: Boolean = false) extends Command
final case class CMD_FAIL_MALFORMED_HTLC(id: Long, onionHash: BinaryData, failureCode: Int, commit: Boolean = false) extends Command
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala
index b8704aee4..e1afe7cc5 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala
@@ -62,9 +62,9 @@ case class Commitments(localParams: LocalParams, remoteParams: RemoteParams,
def hasNoPendingHtlcs: Boolean = localCommit.spec.htlcs.isEmpty && remoteCommit.spec.htlcs.isEmpty && remoteNextCommitInfo.isRight
def hasTimedoutOutgoingHtlcs(blockheight: Long): Boolean =
- localCommit.spec.htlcs.exists(htlc => htlc.direction == OUT && blockheight >= htlc.add.expiry) ||
- remoteCommit.spec.htlcs.exists(htlc => htlc.direction == IN && blockheight >= htlc.add.expiry) ||
- remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.spec.htlcs.exists(htlc => htlc.direction == IN && blockheight >= htlc.add.expiry)).getOrElse(false)
+ localCommit.spec.htlcs.exists(htlc => htlc.direction == OUT && blockheight >= htlc.add.cltvExpiry) ||
+ remoteCommit.spec.htlcs.exists(htlc => htlc.direction == IN && blockheight >= htlc.add.cltvExpiry) ||
+ remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.spec.htlcs.exists(htlc => htlc.direction == IN && blockheight >= htlc.add.cltvExpiry)).getOrElse(false)
def addLocalProposal(proposal: UpdateMessage): Commitments = Commitments.addLocalProposal(this, proposal)
@@ -102,13 +102,13 @@ object Commitments {
val blockCount = Globals.blockCount.get()
// our counterparty needs a reasonable amount of time to pull the funds from downstream before we can get refunded (see BOLT 2 and BOLT 11 for a calculation and rationale)
val minExpiry = blockCount + Channel.MIN_CLTV_EXPIRY
- if (cmd.expiry < minExpiry) {
- return Left(ExpiryTooSmall(commitments.channelId, minimum = minExpiry, actual = cmd.expiry, blockCount = blockCount))
+ if (cmd.cltvExpiry < minExpiry) {
+ return Left(ExpiryTooSmall(commitments.channelId, minimum = minExpiry, actual = cmd.cltvExpiry, blockCount = blockCount))
}
val maxExpiry = blockCount + Channel.MAX_CLTV_EXPIRY
// we don't want to use too high a refund timeout, because our funds will be locked during that time if the payment is never fulfilled
- if (cmd.expiry >= maxExpiry) {
- return Left(ExpiryTooBig(commitments.channelId, maximum = maxExpiry, actual = cmd.expiry, blockCount = blockCount))
+ if (cmd.cltvExpiry >= maxExpiry) {
+ return Left(ExpiryTooBig(commitments.channelId, maximum = maxExpiry, actual = cmd.cltvExpiry, blockCount = blockCount))
}
if (cmd.amountMsat < commitments.remoteParams.htlcMinimumMsat) {
@@ -116,7 +116,7 @@ object Commitments {
}
// let's compute the current commitment *as seen by them* with this change taken into account
- val add = UpdateAddHtlc(commitments.channelId, commitments.localNextHtlcId, cmd.amountMsat, cmd.paymentHash, cmd.expiry, cmd.onion)
+ val add = UpdateAddHtlc(commitments.channelId, commitments.localNextHtlcId, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion)
// we increment the local htlc index and add an entry to the origins map
val commitments1 = addLocalProposal(commitments, add).copy(localNextHtlcId = commitments.localNextHtlcId + 1, originChannels = commitments.originChannels + (add.id -> origin))
// we need to base the next current commitment on the last sig we sent, even if we didn't yet receive their revocation
@@ -540,17 +540,17 @@ object Commitments {
| toLocal: ${commitments.localCommit.spec.toLocalMsat}
| toRemote: ${commitments.localCommit.spec.toRemoteMsat}
| htlcs:
- |${commitments.localCommit.spec.htlcs.map(h => s" ${h.direction} ${h.add.id} ${h.add.expiry}").mkString("\n")}
+ |${commitments.localCommit.spec.htlcs.map(h => s" ${h.direction} ${h.add.id} ${h.add.cltvExpiry}").mkString("\n")}
|remotecommit:
| toLocal: ${commitments.remoteCommit.spec.toLocalMsat}
| toRemote: ${commitments.remoteCommit.spec.toRemoteMsat}
| htlcs:
- |${commitments.remoteCommit.spec.htlcs.map(h => s" ${h.direction} ${h.add.id} ${h.add.expiry}").mkString("\n")}
+ |${commitments.remoteCommit.spec.htlcs.map(h => s" ${h.direction} ${h.add.id} ${h.add.cltvExpiry}").mkString("\n")}
|next remotecommit:
| toLocal: ${commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.spec.toLocalMsat).getOrElse("N/A")}
| toRemote: ${commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.spec.toRemoteMsat).getOrElse("N/A")}
| htlcs:
- |${commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.spec.htlcs.map(h => s" ${h.direction} ${h.add.id} ${h.add.expiry}").mkString("\n")).getOrElse("N/A")}""".stripMargin
+ |${commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.spec.htlcs.map(h => s" ${h.direction} ${h.add.id} ${h.add.cltvExpiry}").mkString("\n")).getOrElse("N/A")}""".stripMargin
}
}
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala b/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala
index 3829f3aa1..03e82ec34 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala
@@ -27,12 +27,12 @@ import fr.acinq.bitcoin.{BinaryData, DeterministicWallet, MilliSatoshi, Protocol
import fr.acinq.eclair.blockchain.EclairWallet
import fr.acinq.eclair.channel._
import fr.acinq.eclair.crypto.TransportHandler
-import fr.acinq.eclair.crypto.TransportHandler
import fr.acinq.eclair.router._
import fr.acinq.eclair.wire._
import fr.acinq.eclair.{wire, _}
import scodec.Attempt
+import scala.compat.Platform
import scala.concurrent.duration._
import scala.util.Random
@@ -73,42 +73,43 @@ class Peer(nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: Actor
stay using d.copy(attempts = attempts + 1)
}
- case Event(Authenticator.Authenticated(_, transport, remoteNodeId, address, outgoing, origin_opt), DisconnectedData(_, channels, _, init)) =>
+ case Event(Authenticator.Authenticated(_, transport, remoteNodeId1, address, outgoing, origin_opt), d: DisconnectedData) =>
+ require(remoteNodeId == remoteNodeId1, s"invalid nodeid: $remoteNodeId != $remoteNodeId1")
log.debug(s"got authenticated connection to $remoteNodeId@${address.getHostString}:${address.getPort}")
transport ! TransportHandler.Listener(self)
context watch transport
- val localInit = init.getOrElse(wire.Init(globalFeatures = nodeParams.globalFeatures, localFeatures = nodeParams.localFeatures))
+ val localInit = d.localInit.getOrElse(wire.Init(globalFeatures = nodeParams.globalFeatures, localFeatures = nodeParams.localFeatures))
transport ! localInit
// we store the ip upon successful outgoing connection, keeping only the most recent one
if (outgoing) {
nodeParams.peersDb.addOrUpdatePeer(remoteNodeId, address)
}
- goto(INITIALIZING) using InitializingData(if (outgoing) Some(address) else None, transport, channels, origin_opt, localInit)
+ goto(INITIALIZING) using InitializingData(if (outgoing) Some(address) else None, transport, d.channels, origin_opt, localInit)
- case Event(Terminated(actor), d@DisconnectedData(_, channels, _, _)) if channels.exists(_._2 == actor) =>
- val h = channels.filter(_._2 == actor).map(_._1)
+ case Event(Terminated(actor), d: DisconnectedData) if d.channels.exists(_._2 == actor) =>
+ val h = d.channels.filter(_._2 == actor).keys
log.info(s"channel closed: channelId=${h.mkString("/")}")
- stay using d.copy(channels = channels -- h)
+ stay using d.copy(channels = d.channels -- h)
case Event(_: wire.LightningMessage, _) => stay // we probably just got disconnected and that's the last messages we received
}
when(INITIALIZING) {
- case Event(remoteInit: wire.Init, InitializingData(address_opt, transport, channels, origin_opt, localInit)) =>
- transport ! TransportHandler.ReadAck(remoteInit)
+ case Event(remoteInit: wire.Init, d: InitializingData) =>
+ d.transport ! TransportHandler.ReadAck(remoteInit)
val remoteHasInitialRoutingSync = Features.hasFeature(remoteInit.localFeatures, Features.INITIAL_ROUTING_SYNC_BIT_OPTIONAL)
val remoteHasChannelRangeQueriesOptional = Features.hasFeature(remoteInit.localFeatures, Features.CHANNEL_RANGE_QUERIES_BIT_OPTIONAL)
val remoteHasChannelRangeQueriesMandatory = Features.hasFeature(remoteInit.localFeatures, Features.CHANNEL_RANGE_QUERIES_BIT_MANDATORY)
val remoteHasChannelRangeQueriesExOptional = Features.hasFeature(remoteInit.localFeatures, Features.CHANNEL_RANGE_QUERIES_EX_BIT_OPTIONAL)
val remoteHasChannelRangeQueriesExMandatory = Features.hasFeature(remoteInit.localFeatures, Features.CHANNEL_RANGE_QUERIES_EX_BIT_MANDATORY)
- val localHasChannelRangeQueriesOptional = Features.hasFeature(localInit.localFeatures, Features.CHANNEL_RANGE_QUERIES_BIT_OPTIONAL)
- val localHasChannelRangeQueriesMandatory = Features.hasFeature(localInit.localFeatures, Features.CHANNEL_RANGE_QUERIES_BIT_MANDATORY)
- val localHasChannelRangeQueriesExOptional = Features.hasFeature(localInit.localFeatures, Features.CHANNEL_RANGE_QUERIES_EX_BIT_OPTIONAL)
- val localHasChannelRangeQueriesExMandatory = Features.hasFeature(localInit.localFeatures, Features.CHANNEL_RANGE_QUERIES_EX_BIT_MANDATORY)
+ val localHasChannelRangeQueriesOptional = Features.hasFeature(d.localInit.localFeatures, Features.CHANNEL_RANGE_QUERIES_BIT_OPTIONAL)
+ val localHasChannelRangeQueriesMandatory = Features.hasFeature(d.localInit.localFeatures, Features.CHANNEL_RANGE_QUERIES_BIT_MANDATORY)
+ val localHasChannelRangeQueriesExOptional = Features.hasFeature(d.localInit.localFeatures, Features.CHANNEL_RANGE_QUERIES_EX_BIT_OPTIONAL)
+ val localHasChannelRangeQueriesExMandatory = Features.hasFeature(d.localInit.localFeatures, Features.CHANNEL_RANGE_QUERIES_EX_BIT_MANDATORY)
log.info(s"$remoteNodeId has features: initialRoutingSync=$remoteHasInitialRoutingSync channelRangeQueriesOptional=$remoteHasChannelRangeQueriesOptional channelRangeQueriesMandatory=$remoteHasChannelRangeQueriesMandatory")
if (Features.areSupported(remoteInit.localFeatures)) {
- origin_opt.map(origin => origin ! "connected")
+ d.origin_opt.foreach(origin => origin ! "connected")
if (remoteHasInitialRoutingSync) {
if (remoteHasChannelRangeQueriesExOptional || remoteHasChannelRangeQueriesExMandatory) {
@@ -125,25 +126,25 @@ class Peer(nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: Actor
// TODO: this is a hack for the Android version: don't send queries if we advertise that we don't support them
if ((localHasChannelRangeQueriesExOptional || localHasChannelRangeQueriesExMandatory) && (remoteHasChannelRangeQueriesExOptional || remoteHasChannelRangeQueriesExMandatory)) {
// if they support extended channel queries, always ask for their filter
- router ! SendChannelQueryEx(remoteNodeId, transport)
+ router ! SendChannelQueryEx(remoteNodeId, d.transport)
} else if ((localHasChannelRangeQueriesOptional || localHasChannelRangeQueriesMandatory) && (remoteHasChannelRangeQueriesOptional || remoteHasChannelRangeQueriesMandatory)) {
// if they support channel queries, always ask for their filter
- router ! SendChannelQuery(remoteNodeId, transport)
+ router ! SendChannelQuery(remoteNodeId, d.transport)
}
// let's bring existing/requested channels online
- channels.values.toSet[ActorRef].foreach(_ ! INPUT_RECONNECTED(transport)) // we deduplicate with toSet because there might be two entries per channel (tmp id and final id)
- goto(CONNECTED) using ConnectedData(address_opt, transport, remoteInit, channels.map { case (k: ChannelId, v) => (k, v) }, localInit = localInit)
+ d.channels.values.toSet[ActorRef].foreach(_ ! INPUT_RECONNECTED(d.transport)) // we deduplicate with toSet because there might be two entries per channel (tmp id and final id)
+ goto(CONNECTED) using ConnectedData(d.address_opt, d.transport, d.localInit, remoteInit, d.channels.map { case (k: ChannelId, v) => (k, v) })
} else {
log.warning(s"incompatible features, disconnecting")
- origin_opt.map(origin => origin ! Status.Failure(new RuntimeException("incompatible features")))
- transport ! PoisonPill
+ d.origin_opt.foreach(origin => origin ! Status.Failure(new RuntimeException("incompatible features")))
+ d.transport ! PoisonPill
stay
}
case Event(Authenticator.Authenticated(connection, _, _, _, _, origin_opt), _) =>
// two connections in parallel
- origin_opt.map(origin => origin ! Status.Failure(new RuntimeException("there is another connection attempt in progress")))
+ origin_opt.foreach(origin => origin ! Status.Failure(new RuntimeException("there is another connection attempt in progress")))
// we kill this one
log.warning(s"killing parallel connection $connection")
connection ! PoisonPill
@@ -155,105 +156,122 @@ class Peer(nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: Actor
context.system.scheduler.scheduleOnce(100 milliseconds, self, o)
stay
- case Event(Terminated(actor), InitializingData(address_opt, transport, channels, _, _)) if actor == transport =>
+ case Event(Terminated(actor), d: InitializingData) if actor == d.transport =>
log.warning(s"lost connection to $remoteNodeId")
- goto(DISCONNECTED) using DisconnectedData(address_opt, channels)
+ goto(DISCONNECTED) using DisconnectedData(d.address_opt, d.channels)
- case Event(Terminated(actor), d@InitializingData(_, _, channels, _, _)) if channels.exists(_._2 == actor) =>
- val h = channels.filter(_._2 == actor).map(_._1)
+ case Event(Terminated(actor), d: InitializingData) if d.channels.exists(_._2 == actor) =>
+ val h = d.channels.filter(_._2 == actor).keys
log.info(s"channel closed: channelId=${h.mkString("/")}")
- stay using d.copy(channels = channels -- h)
+ stay using d.copy(channels = d.channels -- h)
}
- when(CONNECTED, stateTimeout = nodeParams.pingInterval) {
- case Event(StateTimeout, ConnectedData(_, transport, _, _, _, _ ,_)) =>
+ when(CONNECTED) {
+ case Event(SendPing, d: ConnectedData) =>
// no need to use secure random here
val pingSize = Random.nextInt(1000)
val pongSize = Random.nextInt(1000)
- transport ! wire.Ping(pongSize, BinaryData("00" * pingSize))
+ val ping = wire.Ping(pongSize, BinaryData("00" * pingSize))
+ setTimer(PingTimeout.toString, PingTimeout(ping), 10 seconds, repeat = false)
+ d.transport ! ping
+ stay using d.copy(expectedPong_opt = Some(ExpectedPong(ping)))
+
+ case Event(PingTimeout(ping), d: ConnectedData) =>
+ log.warning(s"no response to ping=$ping, closing connection")
+ d.transport ! PoisonPill
stay
- case Event(ping@wire.Ping(pongLength, _), ConnectedData(_, transport, _, _, _, _ ,_)) =>
- transport ! TransportHandler.ReadAck(ping)
- // TODO: (optional) check against the expected data size tat we requested when we sent ping messages
- if (pongLength > 0) {
- transport ! wire.Pong(BinaryData("00" * pongLength))
+ case Event(ping@wire.Ping(pongLength, _), d: ConnectedData) =>
+ d.transport ! TransportHandler.ReadAck(ping)
+ if (pongLength <= 65532) {
+ // see BOLT 1: we reply only if requested pong length is acceptable
+ d.transport ! wire.Pong(BinaryData(Seq.fill[Byte](pongLength)(0.toByte)))
+ } else {
+ log.warning(s"ignoring invalid ping with pongLength=${ping.pongLength}")
}
stay
- case Event(pong@wire.Pong(data), ConnectedData(_, transport, _, _, _, _ ,_)) =>
- transport ! TransportHandler.ReadAck(pong)
- // TODO: compute latency for remote peer ?
- log.debug(s"received pong with ${data.length} bytes")
- stay
+ case Event(pong@wire.Pong(data), d: ConnectedData) =>
+ d.transport ! TransportHandler.ReadAck(pong)
+ d.expectedPong_opt match {
+ case Some(ExpectedPong(ping, timestamp)) if ping.pongLength == data.length =>
+ // we use the pong size to correlate between pings and pongs
+ val latency = Platform.currentTime - timestamp
+ log.debug(s"received pong with latency=$latency")
+ cancelTimer(PingTimeout.toString())
+ schedulePing()
+ case None =>
+ log.debug(s"received unexpected pong with size=${data.length}")
+ }
+ stay using d.copy(expectedPong_opt = None)
- case Event(err@wire.Error(channelId, reason), ConnectedData(_, transport, _, channels, _, _ ,_)) if channelId == CHANNELID_ZERO =>
- transport ! TransportHandler.ReadAck(err)
+ case Event(err@wire.Error(channelId, reason), d: ConnectedData) if channelId == CHANNELID_ZERO =>
+ d.transport ! TransportHandler.ReadAck(err)
log.error(s"connection-level error, failing all channels! reason=${new String(reason)}")
- channels.values.toSet[ActorRef].foreach(_ forward err) // we deduplicate with toSet because there might be two entries per channel (tmp id and final id)
- transport ! PoisonPill
+ d.channels.values.toSet[ActorRef].foreach(_ forward err) // we deduplicate with toSet because there might be two entries per channel (tmp id and final id)
+ d.transport ! PoisonPill
stay
- case Event(err: wire.Error, ConnectedData(_, transport, _, channels, _, _ ,_)) =>
- transport ! TransportHandler.ReadAck(err)
+ case Event(err: wire.Error, d: ConnectedData) =>
+ d.transport ! TransportHandler.ReadAck(err)
// error messages are a bit special because they can contain either temporaryChannelId or channelId (see BOLT 1)
- channels.get(FinalChannelId(err.channelId)).orElse(channels.get(TemporaryChannelId(err.channelId))) match {
+ d.channels.get(FinalChannelId(err.channelId)).orElse(d.channels.get(TemporaryChannelId(err.channelId))) match {
case Some(channel) => channel forward err
- case None => transport ! wire.Error(err.channelId, UNKNOWN_CHANNEL_MESSAGE)
+ case None => d.transport ! wire.Error(err.channelId, UNKNOWN_CHANNEL_MESSAGE)
}
stay
- case Event(c: Peer.OpenChannel, d@ConnectedData(_, transport, remoteInit, channels, _, _ ,_)) =>
+ case Event(c: Peer.OpenChannel, d: ConnectedData) =>
log.info(s"requesting a new channel to $remoteNodeId with fundingSatoshis=${c.fundingSatoshis}, pushMsat=${c.pushMsat} and fundingFeeratePerByte=${c.fundingTxFeeratePerKw_opt}")
val (channel, localParams) = createNewChannel(nodeParams, funder = true, c.fundingSatoshis.toLong, origin_opt = Some(sender))
val temporaryChannelId = randomBytes(32)
val channelFeeratePerKw = Globals.feeratesPerKw.get.blocks_2
val fundingTxFeeratePerKw = c.fundingTxFeeratePerKw_opt.getOrElse(Globals.feeratesPerKw.get.blocks_6)
- channel ! INPUT_INIT_FUNDER(temporaryChannelId, c.fundingSatoshis.amount, c.pushMsat.amount, channelFeeratePerKw, fundingTxFeeratePerKw, localParams, transport, remoteInit, c.channelFlags.getOrElse(nodeParams.channelFlags))
- stay using d.copy(channels = channels + (TemporaryChannelId(temporaryChannelId) -> channel))
+ channel ! INPUT_INIT_FUNDER(temporaryChannelId, c.fundingSatoshis.amount, c.pushMsat.amount, channelFeeratePerKw, fundingTxFeeratePerKw, localParams, d.transport, d.remoteInit, c.channelFlags.getOrElse(nodeParams.channelFlags))
+ stay using d.copy(channels = d.channels + (TemporaryChannelId(temporaryChannelId) -> channel))
- case Event(msg: wire.OpenChannel, d@ConnectedData(_, transport, remoteInit, channels, _, _ ,_)) =>
- transport ! TransportHandler.ReadAck(msg)
- channels.get(TemporaryChannelId(msg.temporaryChannelId)) match {
+ case Event(msg: wire.OpenChannel, d: ConnectedData) =>
+ d.transport ! TransportHandler.ReadAck(msg)
+ d.channels.get(TemporaryChannelId(msg.temporaryChannelId)) match {
case None =>
log.info(s"accepting a new channel to $remoteNodeId")
val (channel, localParams) = createNewChannel(nodeParams, funder = false, fundingSatoshis = msg.fundingSatoshis, origin_opt = None)
val temporaryChannelId = msg.temporaryChannelId
- channel ! INPUT_INIT_FUNDEE(temporaryChannelId, localParams, transport, remoteInit)
+ channel ! INPUT_INIT_FUNDEE(temporaryChannelId, localParams, d.transport, d.remoteInit)
channel ! msg
- stay using d.copy(channels = channels + (TemporaryChannelId(temporaryChannelId) -> channel))
+ stay using d.copy(channels = d.channels + (TemporaryChannelId(temporaryChannelId) -> channel))
case Some(_) =>
log.warning(s"ignoring open_channel with duplicate temporaryChannelId=${msg.temporaryChannelId}")
stay
}
- case Event(msg: wire.HasChannelId, ConnectedData(_, transport, _, channels, _, _, _)) =>
- transport ! TransportHandler.ReadAck(msg)
- channels.get(FinalChannelId(msg.channelId)) match {
+ case Event(msg: wire.HasChannelId, d: ConnectedData) =>
+ d.transport ! TransportHandler.ReadAck(msg)
+ d.channels.get(FinalChannelId(msg.channelId)) match {
case Some(channel) => channel forward msg
- case None => transport ! wire.Error(msg.channelId, UNKNOWN_CHANNEL_MESSAGE)
+ case None => d.transport ! wire.Error(msg.channelId, UNKNOWN_CHANNEL_MESSAGE)
}
stay
- case Event(msg: wire.HasTemporaryChannelId, ConnectedData(_, transport, _, channels, _, _, _)) =>
- transport ! TransportHandler.ReadAck(msg)
- channels.get(TemporaryChannelId(msg.temporaryChannelId)) match {
+ case Event(msg: wire.HasTemporaryChannelId, d: ConnectedData) =>
+ d.transport ! TransportHandler.ReadAck(msg)
+ d.channels.get(TemporaryChannelId(msg.temporaryChannelId)) match {
case Some(channel) => channel forward msg
- case None => transport ! wire.Error(msg.temporaryChannelId, UNKNOWN_CHANNEL_MESSAGE)
+ case None => d.transport ! wire.Error(msg.temporaryChannelId, UNKNOWN_CHANNEL_MESSAGE)
}
stay
- case Event(ChannelIdAssigned(channel, _, temporaryChannelId, channelId), d@ConnectedData(_, _, _, channels, _, _, _)) if channels.contains(TemporaryChannelId(temporaryChannelId)) =>
+ case Event(ChannelIdAssigned(channel, _, temporaryChannelId, channelId), d: ConnectedData) if d.channels.contains(TemporaryChannelId(temporaryChannelId)) =>
log.info(s"channel id switch: previousId=$temporaryChannelId nextId=$channelId")
// NB: we keep the temporary channel id because the switch is not always acknowledged at this point (see https://github.com/lightningnetwork/lightning-rfc/pull/151)
// we won't clean it up, but we won't remember the temporary id on channel termination
- stay using d.copy(channels = channels + (FinalChannelId(channelId) -> channel))
+ stay using d.copy(channels = d.channels + (FinalChannelId(channelId) -> channel))
- case Event(RoutingState(channels, updates, nodes), ConnectedData(_, transport, _, _, _, _, _)) =>
+ case Event(RoutingState(channels, updates, nodes), d: ConnectedData) =>
// let's send the messages
def send(announcements: Iterable[_ <: LightningMessage]) = announcements.foldLeft(0) {
case (c, ann) =>
- transport ! ann
+ d.transport ! ann
c + 1
}
@@ -264,7 +282,7 @@ class Peer(nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: Actor
log.info(s"sent all announcements to {}: channels={} updates={} nodes={}", remoteNodeId, channelsSent, updatesSent, nodesSent)
stay
- case Event(rebroadcast: Rebroadcast, ConnectedData(_, transport, _, _, maybeGossipTimestampFilter, _, _)) =>
+ case Event(rebroadcast: Rebroadcast, d: ConnectedData) =>
/**
* Send and count in a single iteration
@@ -273,11 +291,11 @@ class Peer(nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: Actor
case (count, (_, origins)) if origins.contains(self) =>
// the announcement came from this peer, we don't send it back
count
- case (count, (msg: HasTimestamp, _)) if !timestampInRange(msg, maybeGossipTimestampFilter) =>
+ case (count, (msg: HasTimestamp, _)) if !timestampInRange(msg, d.gossipTimestampFilter) =>
// the peer has set up a filter on timestamp and this message is out of range
count
case (count, (msg, _)) =>
- transport ! msg
+ d.transport ! msg
count + 1
}
@@ -290,7 +308,7 @@ class Peer(nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: Actor
}
stay
- case Event(msg: GossipTimestampFilter, data: ConnectedData) =>
+ case Event(msg: GossipTimestampFilter, d: ConnectedData) =>
// special case: time range filters are peer specific and must not be sent to the router
sender ! TransportHandler.ReadAck(msg)
if (msg.chainHash != nodeParams.chainHash) {
@@ -299,26 +317,26 @@ class Peer(nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: Actor
} else {
log.info(s"setting up gossipTimestampFilter=$msg")
// update their timestamp filter
- stay using data.copy(gossipTimestampFilter = Some(msg))
+ stay using d.copy(gossipTimestampFilter = Some(msg))
}
- case Event(msg: wire.RoutingMessage, ConnectedData(_, transport, _, _, _, _, behavior)) =>
+ case Event(msg: wire.RoutingMessage, d: ConnectedData) =>
msg match {
- case _: ChannelAnnouncement | _: ChannelUpdate | _: NodeAnnouncement if behavior.ignoreNetworkAnnouncement =>
+ case _: ChannelAnnouncement | _: ChannelUpdate | _: NodeAnnouncement if d.behavior.ignoreNetworkAnnouncement =>
// this peer is currently under embargo!
sender ! TransportHandler.ReadAck(msg)
case _ =>
// Note: we don't ack messages here because we don't want them to be stacked in the router's mailbox
- router ! PeerRoutingMessage(transport, remoteNodeId, msg)
+ router ! PeerRoutingMessage(d.transport, remoteNodeId, msg)
}
stay
- case Event(readAck: TransportHandler.ReadAck, c: ConnectedData) =>
+ case Event(readAck: TransportHandler.ReadAck, d: ConnectedData) =>
// we just forward acks from router to transport
- c.transport forward readAck
+ d.transport forward readAck
stay
- case Event(badMessage: BadMessage, data@ConnectedData(_, transport, _, _, _, _, behavior)) =>
+ case Event(badMessage: BadMessage, d: ConnectedData) =>
val behavior1 = badMessage match {
case InvalidSignature(r) =>
val bin: String = LightningMessageCodecs.lightningMessageCodec.encode(r) match {
@@ -327,66 +345,64 @@ class Peer(nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: Actor
}
log.error(s"peer sent us a routing message with invalid sig: r=$r bin=$bin")
// for now we just return an error, maybe ban the peer in the future?
- transport ! Error(CHANNELID_ZERO, s"bad announcement sig! bin=$bin".getBytes())
- behavior
+ d.transport ! Error(CHANNELID_ZERO, s"bad announcement sig! bin=$bin".getBytes())
+ d.behavior
case ChannelClosed(_) =>
- if (behavior.ignoreNetworkAnnouncement) {
+ if (d.behavior.ignoreNetworkAnnouncement) {
// we already are ignoring announcements, we may have additional notifications for announcements that were received right before our ban
- behavior.copy(fundingTxAlreadySpentCount = behavior.fundingTxAlreadySpentCount + 1)
- } else if (behavior.fundingTxAlreadySpentCount < MAX_FUNDING_TX_ALREADY_SPENT) {
- behavior.copy(fundingTxAlreadySpentCount = behavior.fundingTxAlreadySpentCount + 1)
+ d.behavior.copy(fundingTxAlreadySpentCount = d.behavior.fundingTxAlreadySpentCount + 1)
+ } else if (d.behavior.fundingTxAlreadySpentCount < MAX_FUNDING_TX_ALREADY_SPENT) {
+ d.behavior.copy(fundingTxAlreadySpentCount = d.behavior.fundingTxAlreadySpentCount + 1)
} else {
- log.warning(s"peer sent us too many channel announcements with funding tx already spent (count=${behavior.fundingTxAlreadySpentCount + 1}), ignoring network announcements for $IGNORE_NETWORK_ANNOUNCEMENTS_PERIOD")
- import scala.concurrent.ExecutionContext.Implicits.global
- context.system.scheduler.scheduleOnce(IGNORE_NETWORK_ANNOUNCEMENTS_PERIOD, self, ResumeAnnouncements)
- behavior.copy(fundingTxAlreadySpentCount = behavior.fundingTxAlreadySpentCount + 1, ignoreNetworkAnnouncement = true)
+ log.warning(s"peer sent us too many channel announcements with funding tx already spent (count=${d.behavior.fundingTxAlreadySpentCount + 1}), ignoring network announcements for $IGNORE_NETWORK_ANNOUNCEMENTS_PERIOD")
+ setTimer(ResumeAnnouncements.toString, ResumeAnnouncements, IGNORE_NETWORK_ANNOUNCEMENTS_PERIOD, repeat = false)
+ d.behavior.copy(fundingTxAlreadySpentCount = d.behavior.fundingTxAlreadySpentCount + 1, ignoreNetworkAnnouncement = true)
}
case NonexistingChannel(_) =>
// this should never happen, unless we are not in sync or there is a 6+ blocks reorg
- if (behavior.ignoreNetworkAnnouncement) {
+ if (d.behavior.ignoreNetworkAnnouncement) {
// we already are ignoring announcements, we may have additional notifications for announcements that were received right before our ban
- behavior.copy(fundingTxNotFoundCount = behavior.fundingTxNotFoundCount + 1)
- } else if (behavior.fundingTxNotFoundCount < MAX_FUNDING_TX_NOT_FOUND) {
- behavior.copy(fundingTxNotFoundCount = behavior.fundingTxNotFoundCount + 1)
+ d.behavior.copy(fundingTxNotFoundCount = d.behavior.fundingTxNotFoundCount + 1)
+ } else if (d.behavior.fundingTxNotFoundCount < MAX_FUNDING_TX_NOT_FOUND) {
+ d.behavior.copy(fundingTxNotFoundCount = d.behavior.fundingTxNotFoundCount + 1)
} else {
- log.warning(s"peer sent us too many channel announcements with non-existing funding tx (count=${behavior.fundingTxNotFoundCount + 1}), ignoring network announcements for $IGNORE_NETWORK_ANNOUNCEMENTS_PERIOD")
- import scala.concurrent.ExecutionContext.Implicits.global
- context.system.scheduler.scheduleOnce(IGNORE_NETWORK_ANNOUNCEMENTS_PERIOD, self, ResumeAnnouncements)
- behavior.copy(fundingTxNotFoundCount = behavior.fundingTxNotFoundCount + 1, ignoreNetworkAnnouncement = true)
+ log.warning(s"peer sent us too many channel announcements with non-existing funding tx (count=${d.behavior.fundingTxNotFoundCount + 1}), ignoring network announcements for $IGNORE_NETWORK_ANNOUNCEMENTS_PERIOD")
+ setTimer(ResumeAnnouncements.toString, ResumeAnnouncements, IGNORE_NETWORK_ANNOUNCEMENTS_PERIOD, repeat = false)
+ d.behavior.copy(fundingTxNotFoundCount = d.behavior.fundingTxNotFoundCount + 1, ignoreNetworkAnnouncement = true)
}
}
- stay using data.copy(behavior = behavior1)
+ stay using d.copy(behavior = behavior1)
- case Event(ResumeAnnouncements, data: ConnectedData) =>
+ case Event(ResumeAnnouncements, d: ConnectedData) =>
log.info(s"resuming processing of network announcements for peer")
- stay using data.copy(behavior = data.behavior.copy(fundingTxAlreadySpentCount = 0, ignoreNetworkAnnouncement = false))
+ stay using d.copy(behavior = d.behavior.copy(fundingTxAlreadySpentCount = 0, ignoreNetworkAnnouncement = false))
- case Event(Disconnect, c:ConnectedData) =>
- c.transport ! PoisonPill
+ case Event(Disconnect, d: ConnectedData) =>
+ d.transport ! PoisonPill
stay
- case Event(Terminated(actor), ConnectedData(address_opt, transport, _, channels, _, _, _)) if actor == transport =>
+ case Event(Terminated(actor), d: ConnectedData) if actor == d.transport =>
log.info(s"lost connection to $remoteNodeId")
- channels.values.toSet[ActorRef].foreach(_ ! INPUT_DISCONNECTED) // we deduplicate with toSet because there might be two entries per channel (tmp id and final id)
- goto(DISCONNECTED) using DisconnectedData(address_opt, channels.collect { case (k: FinalChannelId, v) => (k, v) })
+ d.channels.values.toSet[ActorRef].foreach(_ ! INPUT_DISCONNECTED) // we deduplicate with toSet because there might be two entries per channel (tmp id and final id)
+ goto(DISCONNECTED) using DisconnectedData(d.address_opt, d.channels.collect { case (k: FinalChannelId, v) => (k, v) })
- case Event(Terminated(actor), d@ConnectedData(_, transport, _, channels, _, _, _)) if channels.values.toSet.contains(actor) =>
+ case Event(Terminated(actor), d: ConnectedData) if d.channels.values.toSet.contains(actor) =>
// we will have at most 2 ids: a TemporaryChannelId and a FinalChannelId
- val channelIds = channels.filter(_._2 == actor).keys
+ val channelIds = d.channels.filter(_._2 == actor).keys
log.info(s"channel closed: channelId=${channelIds.mkString("/")}")
- if (channels.values.toSet - actor == Set.empty) {
+ if (d.channels.values.toSet - actor == Set.empty) {
log.info(s"that was the last open channel, closing the connection")
- transport ! PoisonPill
+ d.transport ! PoisonPill
}
- stay using d.copy(channels = channels -- channelIds)
+ stay using d.copy(channels = d.channels -- channelIds)
- case Event(h: Authenticator.Authenticated, ConnectedData(address_opt, oldTransport, _, channels, _, _, _)) =>
+ case Event(h: Authenticator.Authenticated, d: ConnectedData) =>
log.info(s"got new transport while already connected, switching to new transport")
- context unwatch oldTransport
- oldTransport ! PoisonPill
- channels.values.toSet[ActorRef].foreach(_ ! INPUT_DISCONNECTED) // we deduplicate with toSet because there might be two entries per channel (tmp id and final id)
+ context unwatch d.transport
+ d.transport ! PoisonPill
+ d.channels.values.toSet[ActorRef].foreach(_ ! INPUT_DISCONNECTED) // we deduplicate with toSet because there might be two entries per channel (tmp id and final id)
self ! h
- goto(DISCONNECTED) using DisconnectedData(address_opt, channels.collect { case (k: FinalChannelId, v) => (k, v) })
+ goto(DISCONNECTED) using DisconnectedData(d.address_opt, d.channels.collect { case (k: FinalChannelId, v) => (k, v) })
}
whenUnhandled {
@@ -403,11 +419,16 @@ class Peer(nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: Actor
stay
case Event(_: TransportHandler.ReadAck, _) => stay // ignored
+
+ case Event(SendPing, _) => stay // we got disconnected in the meantime
+
+ case Event(_: Pong, _) => stay // we got disconnected before receiving the pong
}
onTransition {
case _ -> DISCONNECTED if nodeParams.autoReconnect && nextStateData.address_opt.isDefined => setTimer(RECONNECT_TIMER, Reconnect, 1 second, repeat = false)
case DISCONNECTED -> _ if nodeParams.autoReconnect && stateData.address_opt.isDefined => cancelTimer(RECONNECT_TIMER)
+ case _ -> CONNECTED => schedulePing()
}
def createNewChannel(nodeParams: NodeParams, funder: Boolean, fundingSatoshis: Long, origin_opt: Option[ActorRef]): (ActorRef, LocalParams) = {
@@ -430,6 +451,12 @@ class Peer(nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: Actor
override def mdc(currentMessage: Any): MDC = Logs.mdc(remoteNodeId_opt = Some(remoteNodeId))
+ def schedulePing(): Unit = {
+ // pings are periodically with some randomization
+ val nextDelay = nodeParams.pingInterval + secureRandom.nextInt(10).seconds
+ setTimer(SendPing.toString, SendPing, nextDelay, repeat = false)
+ }
+
}
object Peer {
@@ -461,7 +488,9 @@ object Peer {
case class Nothing() extends Data { override def address_opt = None; override def channels = Map.empty }
case class DisconnectedData(address_opt: Option[InetSocketAddress], channels: Map[FinalChannelId, ActorRef], attempts: Int = 0, localInit: Option[wire.Init] = None) extends Data
case class InitializingData(address_opt: Option[InetSocketAddress], transport: ActorRef, channels: Map[FinalChannelId, ActorRef], origin_opt: Option[ActorRef], localInit: wire.Init) extends Data
- case class ConnectedData(address_opt: Option[InetSocketAddress], transport: ActorRef, remoteInit: wire.Init, channels: Map[ChannelId, ActorRef], gossipTimestampFilter: Option[GossipTimestampFilter] = None, localInit: wire.Init, behavior: Behavior = Behavior()) extends Data
+ case class ConnectedData(address_opt: Option[InetSocketAddress], transport: ActorRef, localInit: wire.Init, remoteInit: wire.Init, channels: Map[ChannelId, ActorRef], gossipTimestampFilter: Option[GossipTimestampFilter] = None, behavior: Behavior = Behavior(), expectedPong_opt: Option[ExpectedPong] = None) extends Data
+ case class ExpectedPong(ping: Ping, timestamp: Long = Platform.currentTime)
+ case class PingTimeout(ping: Ping)
sealed trait State
case object INSTANTIATING extends State
@@ -482,6 +511,7 @@ object Peer {
require(fundingTxFeeratePerKw_opt.getOrElse(0L) >= 0, s"funding tx feerate must be positive")
}
case object GetPeerInfo
+ case object SendPing
case class PeerInfo(nodeId: PublicKey, state: String, address: Option[InetSocketAddress], channels: Int)
case class PeerRoutingMessage(transport: ActorRef, remoteNodeId: PublicKey, message: RoutingMessage)
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/LocalPaymentHandler.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/LocalPaymentHandler.scala
index 2eef926c3..b75b6ee73 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/LocalPaymentHandler.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/LocalPaymentHandler.scala
@@ -30,23 +30,25 @@ import scala.concurrent.duration._
import scala.util.Try
/**
+ * Simple payment handler that generates payment requests and fulfills incoming htlcs.
+ *
+ * Note that unfulfilled payment requests are kept forever if they don't have an expiry!
+ *
* Created by PM on 17/06/2016.
*/
class LocalPaymentHandler(nodeParams: NodeParams) extends Actor with ActorLogging {
- implicit val ec: ExecutionContext = context.system.dispatcher
+ import LocalPaymentHandler._
- context.system.scheduler.schedule(10 minutes, 10 minutes)(self ! Platform.currentTime / 1000)
+ implicit val ec: ExecutionContext = context.system.dispatcher
+ context.system.scheduler.schedule(10 minutes, 10 minutes)(self ! PurgeExpiredRequests)
override def receive: Receive = run(Map.empty)
- def run(hash2preimage: Map[BinaryData, (BinaryData, PaymentRequest)]): Receive = {
+ def run(hash2preimage: Map[BinaryData, PendingPaymentRequest]): Receive = {
- case currentSeconds: Long =>
- context.become(run(hash2preimage.collect {
- case e@(_, (_, pr)) if pr.expiry.isEmpty => e // requests that don't expire are kept forever
- case e@(_, (_, pr)) if pr.timestamp + pr.expiry.get > currentSeconds => e // clean up expired requests
- }))
+ case PurgeExpiredRequests =>
+ context.become(run(hash2preimage.filterNot { case (_, pr) => hasExpired(pr) }))
case ReceivePayment(amount_opt, desc, expirySeconds_opt, extraHops) =>
Try {
@@ -59,7 +61,7 @@ class LocalPaymentHandler(nodeParams: NodeParams) extends Actor with ActorLoggin
val paymentRequest = PaymentRequest(nodeParams.chainHash, amount_opt, paymentHash, nodeParams.privateKey, desc, fallbackAddress = None, expirySeconds = Some(expirySeconds), extraHops = extraHops)
log.debug(s"generated payment request=${PaymentRequest.write(paymentRequest)} from amount=$amount_opt")
sender ! paymentRequest
- context.become(run(hash2preimage + (paymentHash -> (paymentPreimage, paymentRequest))))
+ context.become(run(hash2preimage + (paymentHash -> PendingPaymentRequest(paymentPreimage, paymentRequest))))
} recover { case t => sender ! Status.Failure(t) }
case CheckPayment(paymentHash) =>
@@ -69,14 +71,17 @@ class LocalPaymentHandler(nodeParams: NodeParams) extends Actor with ActorLoggin
}
case htlc: UpdateAddHtlc =>
- hash2preimage.get(htlc.paymentHash) match {
- case Some((paymentPreimage, paymentRequest)) =>
+ hash2preimage
+ .get(htlc.paymentHash) // we retrieve the request
+ .filterNot(hasExpired) // and filter it out if it is expired (it will be purged independently)
+ match {
+ case Some(PendingPaymentRequest(paymentPreimage, paymentRequest)) =>
val minFinalExpiry = Globals.blockCount.get() + paymentRequest.minFinalCltvExpiry.getOrElse(Channel.MIN_CLTV_EXPIRY)
// The htlc amount must be equal or greater than the requested amount. A slight overpaying is permitted, however
// it must not be greater than two times the requested amount.
// see https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md#failure-messages
paymentRequest.amount match {
- case _ if htlc.expiry < minFinalExpiry =>
+ case _ if htlc.cltvExpiry < minFinalExpiry =>
sender ! CMD_FAIL_HTLC(htlc.id, Right(FinalExpiryTooSoon), commit = true)
case Some(amount) if MilliSatoshi(htlc.amountMsat) < amount =>
log.warning(s"received payment with amount too small for paymentHash=${htlc.paymentHash} amountMsat=${htlc.amountMsat}")
@@ -95,9 +100,25 @@ class LocalPaymentHandler(nodeParams: NodeParams) extends Actor with ActorLoggin
case None =>
sender ! CMD_FAIL_HTLC(htlc.id, Right(UnknownPaymentHash), commit = true)
}
+
+ case 'requests =>
+ // this is just for testing
+ sender ! hash2preimage
}
+
}
object LocalPaymentHandler {
+
def props(nodeParams: NodeParams): Props = Props(new LocalPaymentHandler(nodeParams))
+
+ case object PurgeExpiredRequests
+
+ case class PendingPaymentRequest(preimage: BinaryData, paymentRequest: PaymentRequest)
+
+ def hasExpired(pr: PendingPaymentRequest): Boolean = pr.paymentRequest.expiry match {
+ case Some(expiry) => pr.paymentRequest.timestamp + expiry <= Platform.currentTime / 1000
+ case None => false // this request will never expire
+ }
+
}
\ No newline at end of file
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Relayer.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Relayer.scala
index afee2cdfa..9d6a4e9f8 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Relayer.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Relayer.scala
@@ -200,7 +200,7 @@ object Relayer {
case class FinalPayload(add: UpdateAddHtlc, payload: PerHopPayload) extends NextPayload
case class RelayPayload(add: UpdateAddHtlc, payload: PerHopPayload, nextPacket: Sphinx.Packet) extends NextPayload {
val relayFeeSatoshi = add.amountMsat - payload.amtToForward
- val expiryDelta = add.expiry - payload.outgoingCltvValue
+ val expiryDelta = add.cltvExpiry - payload.outgoingCltvValue
}
// @formatter:on
@@ -240,8 +240,8 @@ object Relayer {
finalPayload.payload match {
case PerHopPayload(_, finalAmountToForward, _) if finalAmountToForward > add.amountMsat =>
Left(CMD_FAIL_HTLC(add.id, Right(FinalIncorrectHtlcAmount(add.amountMsat)), commit = true))
- case PerHopPayload(_, _, finalOutgoingCltvValue) if finalOutgoingCltvValue != add.expiry =>
- Left(CMD_FAIL_HTLC(add.id, Right(FinalIncorrectCltvExpiry(add.expiry)), commit = true))
+ case PerHopPayload(_, _, finalOutgoingCltvValue) if finalOutgoingCltvValue != add.cltvExpiry =>
+ Left(CMD_FAIL_HTLC(add.id, Right(FinalIncorrectCltvExpiry(add.cltvExpiry)), commit = true))
case _ =>
Right(add)
}
@@ -265,7 +265,7 @@ object Relayer {
case Some(channelUpdate) if payload.amtToForward < channelUpdate.htlcMinimumMsat =>
Left(CMD_FAIL_HTLC(add.id, Right(AmountBelowMinimum(add.amountMsat, channelUpdate)), commit = true))
case Some(channelUpdate) if relayPayload.expiryDelta != channelUpdate.cltvExpiryDelta =>
- Left(CMD_FAIL_HTLC(add.id, Right(IncorrectCltvExpiry(add.expiry, channelUpdate)), commit = true))
+ Left(CMD_FAIL_HTLC(add.id, Right(IncorrectCltvExpiry(add.cltvExpiry, channelUpdate)), commit = true))
case Some(channelUpdate) if relayPayload.relayFeeSatoshi < nodeFee(channelUpdate.feeBaseMsat, channelUpdate.feeProportionalMillionths, payload.amtToForward) =>
Left(CMD_FAIL_HTLC(add.id, Right(FeeInsufficient(add.amountMsat, channelUpdate)), commit = true))
case Some(channelUpdate) =>
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala b/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala
index c621e4d20..fd435aab2 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala
@@ -262,8 +262,8 @@ class Router(nodeParams: NodeParams, watcher: ActorRef) extends FSMDiagnosticAct
val ignoredUpdates = getIgnoredChannelDesc(d.updates ++ d.privateUpdates ++ assistedUpdates, ignoreNodes) ++ ignoreChannels ++ d.excludedChannels
log.info(s"finding a route $start->$end with assistedChannels={} ignoreNodes={} ignoreChannels={} excludedChannels={}", assistedUpdates.keys.mkString(","), ignoreNodes.map(_.toBin).mkString(","), ignoreChannels.mkString(","), d.excludedChannels.mkString(","))
findRoute(d.graph, start, end, withEdges = assistedUpdates, withoutEdges = ignoredUpdates)
- .map(r => sender ! RouteResponse(r, ignoreNodes, ignoreChannels))
- .recover { case t => sender ! Status.Failure(t) }
+ .map(r => sender ! RouteResponse(r, ignoreNodes, ignoreChannels))
+ .recover { case t => sender ! Status.Failure(t) }
stay
case Event(SendChannelQuery(remoteNodeId, remote), d) =>
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala b/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala
index b98be95b1..ae3073fde 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala
@@ -200,7 +200,7 @@ object Transactions {
val htlcOfferedOutputs = trimOfferedHtlcs(localDustLimit, spec)
.map(htlc => TxOut(MilliSatoshi(htlc.add.amountMsat), pay2wsh(htlcOffered(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.add.paymentHash)))))
val htlcReceivedOutputs = trimReceivedHtlcs(localDustLimit, spec)
- .map(htlc => TxOut(MilliSatoshi(htlc.add.amountMsat), pay2wsh(htlcReceived(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.add.paymentHash), htlc.add.expiry))))
+ .map(htlc => TxOut(MilliSatoshi(htlc.add.amountMsat), pay2wsh(htlcReceived(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.add.paymentHash), htlc.add.cltvExpiry))))
val txnumber = obscuredCommitTxNumber(commitTxNumber, localIsFunder, localPaymentBasePoint, remotePaymentBasePoint)
val (sequence, locktime) = encodeTxNumber(txnumber)
@@ -227,12 +227,12 @@ object Transactions {
version = 2,
txIn = TxIn(input.outPoint, Array.emptyByteArray, 0x00000000L) :: Nil,
txOut = TxOut(amount, pay2wsh(toLocalDelayed(localRevocationPubkey, toLocalDelay, localDelayedPaymentPubkey))) :: Nil,
- lockTime = htlc.expiry))
+ lockTime = htlc.cltvExpiry))
}
def makeHtlcSuccessTx(commitTx: Transaction, outputsAlreadyUsed: Set[Int], localDustLimit: Satoshi, localRevocationPubkey: PublicKey, toLocalDelay: Int, localDelayedPaymentPubkey: PublicKey, localHtlcPubkey: PublicKey, remoteHtlcPubkey: PublicKey, feeratePerKw: Long, htlc: UpdateAddHtlc): HtlcSuccessTx = {
val fee = weight2fee(feeratePerKw, htlcSuccessWeight)
- val redeemScript = htlcReceived(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.paymentHash), htlc.expiry)
+ val redeemScript = htlcReceived(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.paymentHash), htlc.cltvExpiry)
val pubkeyScript = write(pay2wsh(redeemScript))
val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(Satoshi(htlc.amountMsat / 1000)))
val amount = MilliSatoshi(htlc.amountMsat) - fee
@@ -286,7 +286,7 @@ object Transactions {
}
def makeClaimHtlcTimeoutTx(commitTx: Transaction, outputsAlreadyUsed: Set[Int], localDustLimit: Satoshi, localHtlcPubkey: PublicKey, remoteHtlcPubkey: PublicKey, remoteRevocationPubkey: PublicKey, localFinalScriptPubKey: BinaryData, htlc: UpdateAddHtlc, feeratePerKw: Long): ClaimHtlcTimeoutTx = {
- val redeemScript = htlcReceived(remoteHtlcPubkey, localHtlcPubkey, remoteRevocationPubkey, ripemd160(htlc.paymentHash), htlc.expiry)
+ val redeemScript = htlcReceived(remoteHtlcPubkey, localHtlcPubkey, remoteRevocationPubkey, ripemd160(htlc.paymentHash), htlc.cltvExpiry)
val pubkeyScript = write(pay2wsh(redeemScript))
val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(Satoshi(htlc.amountMsat / 1000)))
val input = InputInfo(OutPoint(commitTx, outputIndex), commitTx.txOut(outputIndex), write(redeemScript))
@@ -296,7 +296,7 @@ object Transactions {
version = 2,
txIn = TxIn(input.outPoint, Array.emptyByteArray, 0x00000000L) :: Nil,
txOut = TxOut(Satoshi(0), localFinalScriptPubKey) :: Nil,
- lockTime = htlc.expiry)
+ lockTime = htlc.cltvExpiry)
val weight = Transactions.addSigs(ClaimHtlcTimeoutTx(input, tx), BinaryData("00" * 73)).tx.weight()
val fee = weight2fee(feeratePerKw, weight)
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageTypes.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageTypes.scala
index 469460eb7..45309fde4 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageTypes.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageTypes.scala
@@ -112,7 +112,7 @@ case class UpdateAddHtlc(channelId: BinaryData,
id: Long,
amountMsat: Long,
paymentHash: BinaryData,
- expiry: Long,
+ cltvExpiry: Long,
onionRoutingPacket: BinaryData) extends HtlcMessage with UpdateMessage with HasChannelId
case class UpdateFulfillHtlc(channelId: BinaryData,
diff --git a/eclair-core/src/test/resources/integration/bitcoin.conf b/eclair-core/src/test/resources/integration/bitcoin.conf
index 25e7711fe..da4dd59a0 100644
--- a/eclair-core/src/test/resources/integration/bitcoin.conf
+++ b/eclair-core/src/test/resources/integration/bitcoin.conf
@@ -6,6 +6,6 @@ rpcuser=foo
rpcpassword=bar
txindex=1
zmqpubrawblock=tcp://127.0.0.1:28334
-zmqpubrawtx=tcp://127.0.0.1:28334
+zmqpubrawtx=tcp://127.0.0.1:28335
rpcworkqueue=64
addresstype=bech32
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/TestBitcoinClient.scala b/eclair-core/src/test/scala/fr/acinq/eclair/TestBitcoinClient.scala
index 6d33a6e69..68e3c0464 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/TestBitcoinClient.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/TestBitcoinClient.scala
@@ -19,7 +19,7 @@ package fr.acinq.eclair
import akka.actor.ActorSystem
import fr.acinq.bitcoin.{Block, Transaction}
import fr.acinq.eclair.blockchain._
-import fr.acinq.eclair.blockchain.bitcoind.rpc.{BitcoinJsonRPCClient, ExtendedBitcoinClient}
+import fr.acinq.eclair.blockchain.bitcoind.rpc.{BasicBitcoinJsonRPCClient, BitcoinJsonRPCClient, ExtendedBitcoinClient}
import org.json4s.JsonAST
import scala.concurrent.duration._
@@ -28,9 +28,7 @@ import scala.concurrent.{ExecutionContext, Future}
/**
* Created by PM on 26/04/2016.
*/
-class TestBitcoinClient()(implicit system: ActorSystem) extends ExtendedBitcoinClient(new BitcoinJsonRPCClient {
- override def invoke(method: String, params: Any*)(implicit ec: ExecutionContext): Future[JsonAST.JValue] = ???
-}) {
+class TestBitcoinClient()(implicit system: ActorSystem) extends ExtendedBitcoinClient(new BasicBitcoinJsonRPCClient("", "", "", 0)(http = null)) {
import scala.concurrent.ExecutionContext.Implicits.global
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreWalletSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreWalletSpec.scala
index 575e9e512..963df9ebd 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreWalletSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreWalletSpec.scala
@@ -48,7 +48,6 @@ class BitcoinCoreWalletSpec extends TestKit(ActorSystem("test")) with BitcoindSe
implicit val formats = DefaultFormats.withBigDecimal
-
override def beforeAll(): Unit = {
startBitcoind()
}
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoindService.scala b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoindService.scala
index 67a171abb..8c9cc28d2 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoindService.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoindService.scala
@@ -23,6 +23,8 @@ import java.util.UUID
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.pattern.pipe
import akka.testkit.{TestKitBase, TestProbe}
+import com.softwaremill.sttp.okhttp.OkHttpFutureBackend
+import com.softwaremill.sttp.okhttp.OkHttpFutureBackend
import fr.acinq.eclair.blockchain.bitcoind.rpc.{BasicBitcoinJsonRPCClient, BitcoinJsonRPCClient}
import fr.acinq.eclair.integration.IntegrationSpec
import grizzled.slf4j.Logging
@@ -35,6 +37,7 @@ trait BitcoindService extends Logging {
self: TestKitBase =>
implicit val system: ActorSystem
+ implicit val sttpBackend = OkHttpFutureBackend()
import scala.sys.process._
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/electrum/ElectrumWalletSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/electrum/ElectrumWalletSpec.scala
index 02b286256..e423c1899 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/electrum/ElectrumWalletSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/electrum/ElectrumWalletSpec.scala
@@ -28,6 +28,7 @@ import fr.acinq.eclair.blockchain.bitcoind.{BitcoinCoreWallet, BitcoindService}
import fr.acinq.eclair.blockchain.electrum.ElectrumClient.{BroadcastTransaction, BroadcastTransactionResponse}
import grizzled.slf4j.Logging
import org.json4s.JsonAST.{JDecimal, JString, JValue}
+import org.scalatest.junit.JUnitRunner
import org.scalatest.{BeforeAndAfterAll, FunSuiteLike}
import scala.concurrent.Await
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/fee/EarnDotComFeeProviderSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/fee/EarnDotComFeeProviderSpec.scala
index a0c79750e..fb3c9c85a 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/fee/EarnDotComFeeProviderSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/fee/EarnDotComFeeProviderSpec.scala
@@ -16,8 +16,8 @@
package fr.acinq.eclair.blockchain.fee
-import akka.actor.ActorSystem
import akka.util.Timeout
+import com.softwaremill.sttp.okhttp.OkHttpFutureBackend
import grizzled.slf4j.Logging
import org.json4s.DefaultFormats
import org.scalatest.FunSuite
@@ -70,7 +70,7 @@ class EarnDotComFeeProviderSpec extends FunSuite with Logging {
test("make sure API hasn't changed") {
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
- implicit val system = ActorSystem()
+ implicit val sttpBackend = OkHttpFutureBackend()
implicit val timeout = Timeout(30 seconds)
val provider = new EarnDotComFeeProvider()
logger.info("earn.com livenet fees: " + Await.result(provider.getFeerates, 10 seconds))
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala
index 626e8b9e7..d5873ea69 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala
@@ -90,8 +90,8 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
val sender = TestProbe()
val h = BinaryData("42" * 32)
- val originHtlc = UpdateAddHtlc(channelId = "42" * 32, id = 5656, amountMsat = 50000000, expiry = 400144, paymentHash = h, onionRoutingPacket = "00" * 1254)
- val cmd = CMD_ADD_HTLC(originHtlc.amountMsat - 10000, h, originHtlc.expiry - 7, upstream_opt = Some(originHtlc))
+ val originHtlc = UpdateAddHtlc(channelId = "42" * 32, id = 5656, amountMsat = 50000000, cltvExpiry = 400144, paymentHash = h, onionRoutingPacket = "00" * 1254)
+ val cmd = CMD_ADD_HTLC(originHtlc.amountMsat - 10000, h, originHtlc.cltvExpiry - 7, upstream_opt = Some(originHtlc))
sender.send(alice, cmd)
sender.expectMsg("ok")
val htlc = alice2bob.expectMsgType[UpdateAddHtlc]
@@ -108,7 +108,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
import f._
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
val sender = TestProbe()
- val add = CMD_ADD_HTLC(500000000, "11" * 42, expiry = 400144)
+ val add = CMD_ADD_HTLC(500000000, "11" * 42, cltvExpiry = 400144)
sender.send(alice, add)
val error = InvalidPaymentHash(channelId(alice))
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
@@ -121,7 +121,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
val currentBlockCount = Globals.blockCount.get
val expiryTooSmall = currentBlockCount + 3
- val add = CMD_ADD_HTLC(500000000, "11" * 32, expiry = expiryTooSmall)
+ val add = CMD_ADD_HTLC(500000000, "11" * 32, cltvExpiry = expiryTooSmall)
sender.send(alice, add)
val error = ExpiryTooSmall(channelId(alice), currentBlockCount + Channel.MIN_CLTV_EXPIRY, expiryTooSmall, currentBlockCount)
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
@@ -134,7 +134,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
val currentBlockCount = Globals.blockCount.get
val expiryTooBig = currentBlockCount + Channel.MAX_CLTV_EXPIRY + 1
- val add = CMD_ADD_HTLC(500000000, "11" * 32, expiry = expiryTooBig)
+ val add = CMD_ADD_HTLC(500000000, "11" * 32, cltvExpiry = expiryTooBig)
sender.send(alice, add)
val error = ExpiryTooBig(channelId(alice), maximum = currentBlockCount + Channel.MAX_CLTV_EXPIRY, actual = expiryTooBig, blockCount = currentBlockCount)
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
@@ -257,7 +257,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].localShutdown.isDefined && !alice.stateData.asInstanceOf[DATA_NORMAL].remoteShutdown.isDefined)
// actual test starts here
- val add = CMD_ADD_HTLC(500000000, "11" * 32, expiry = 400144)
+ val add = CMD_ADD_HTLC(500000000, "11" * 32, cltvExpiry = 400144)
sender.send(alice, add)
val error = NoMoreHtlcsClosingInProgress(channelId(alice))
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
@@ -269,14 +269,14 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
val sender = TestProbe()
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
// let's make alice send an htlc
- val add1 = CMD_ADD_HTLC(500000000, "11" * 32, expiry = 400144)
+ val add1 = CMD_ADD_HTLC(500000000, "11" * 32, cltvExpiry = 400144)
sender.send(alice, add1)
sender.expectMsg("ok")
// at the same time bob initiates a closing
sender.send(bob, CMD_CLOSE(None))
sender.expectMsg("ok")
// this command will be received by alice right after having received the shutdown
- val add2 = CMD_ADD_HTLC(100000000, "22" * 32, expiry = 300000)
+ val add2 = CMD_ADD_HTLC(100000000, "22" * 32, cltvExpiry = 300000)
// messages cross
alice2bob.expectMsgType[UpdateAddHtlc]
alice2bob.forward(bob)
@@ -328,7 +328,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
test("recv UpdateAddHtlc (value too small)") { f =>
import f._
val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx
- val htlc = UpdateAddHtlc("00" * 32, 0, 150, BinaryData("42" * 32), expiry = 400144, defaultOnion)
+ val htlc = UpdateAddHtlc("00" * 32, 0, 150, BinaryData("42" * 32), cltvExpiry = 400144, defaultOnion)
alice2bob.forward(bob, htlc)
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data) === HtlcValueTooSmall(channelId(bob), minimum = 1000, actual = 150).getMessage)
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala
index d1a7d7680..ac78f50c1 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala
@@ -97,7 +97,7 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
test("recv CMD_ADD_HTLC") { f =>
import f._
val sender = TestProbe()
- val add = CMD_ADD_HTLC(500000000, "11" * 32, expiry = 300000)
+ val add = CMD_ADD_HTLC(500000000, "11" * 32, cltvExpiry = 300000)
sender.send(alice, add)
val error = ChannelUnavailable(channelId(alice))
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(Some(sender.ref)), None, Some(add))))
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/g/NegotiatingStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/g/NegotiatingStateSpec.scala
index 7de370327..9c3bb2b1f 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/g/NegotiatingStateSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/g/NegotiatingStateSpec.scala
@@ -69,7 +69,7 @@ class NegotiatingStateSpec extends TestkitBaseClass with StateTestsHelperMethods
import f._
alice2bob.expectMsgType[ClosingSigned]
val sender = TestProbe()
- val add = CMD_ADD_HTLC(500000000, "11" * 32, expiry = 300000)
+ val add = CMD_ADD_HTLC(500000000, "11" * 32, cltvExpiry = 300000)
sender.send(alice, add)
val error = ChannelUnavailable(channelId(alice))
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(Some(sender.ref)), None, Some(add))))
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala
index c1707edab..810c7974a 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala
@@ -110,7 +110,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
// actual test starts here
val sender = TestProbe()
- val add = CMD_ADD_HTLC(500000000, "11" * 32, expiry = 300000)
+ val add = CMD_ADD_HTLC(500000000, "11" * 32, cltvExpiry = 300000)
sender.send(alice, add)
val error = ChannelUnavailable(channelId(alice))
sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(Some(sender.ref)), None, Some(add))))
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteNetworkDbSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteNetworkDbSpec.scala
index bd76ef09b..e0f0f9f62 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteNetworkDbSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteNetworkDbSpec.scala
@@ -85,7 +85,7 @@ class SqliteNetworkDbSpec extends FunSuite {
val channel_update_1 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, ShortChannelId(42), 5, 7000000, 50000, 100, 500000000L, true)
val channel_update_2 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, ShortChannelId(43), 5, 7000000, 50000, 100, 500000000L, true)
- val channel_update_3 = ChannelUpdate(BinaryData.empty, Block.RegtestGenesisBlock.hash, ShortChannelId(44), 123456789, Announcements.makeMessageFlags(hasOptionChannelHtlcMax = false), Announcements.makeChannelFlags(isNode1 = true, enable = true), 5, 7000000, 50000, 100, None)
+ val channel_update_3 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, ShortChannelId(44), 5, 7000000, 50000, 100, 500000000L, true)
assert(db.listChannelUpdates().toSet === Set.empty)
db.addChannelUpdate(channel_update_1)
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/integration/IntegrationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/integration/IntegrationSpec.scala
index a0e76e7bc..28c441f82 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/integration/IntegrationSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/integration/IntegrationSpec.scala
@@ -19,7 +19,8 @@ package fr.acinq.eclair.integration
import java.io.{File, PrintWriter}
import java.util.Properties
-import akka.actor.{ActorRef, ActorSystem}
+import akka.actor.{ActorRef, ActorSystem, Terminated}
+import akka.pattern.pipe
import akka.testkit.{TestKit, TestProbe}
import com.google.common.net.HostAndPort
import com.typesafe.config.{Config, ConfigFactory}
@@ -108,7 +109,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService
test("starting eclair nodes") {
import collection.JavaConversions._
- val commonConfig = ConfigFactory.parseMap(Map("eclair.chain" -> "regtest", "eclair.spv" -> false, "eclair.server.public-ips.1" -> "127.0.0.1", "eclair.bitcoind.port" -> 28333, "eclair.bitcoind.rpcport" -> 28332, "eclair.bitcoind.zmq" -> "tcp://127.0.0.1:28334", "eclair.mindepth-blocks" -> 2, "eclair.max-htlc-value-in-flight-msat" -> 100000000000L, "eclair.router-broadcast-interval" -> "2 second", "eclair.auto-reconnect" -> false))
+ val commonConfig = ConfigFactory.parseMap(Map("eclair.chain" -> "regtest", "eclair.spv" -> false, "eclair.server.public-ips.1" -> "127.0.0.1", "eclair.bitcoind.port" -> 28333, "eclair.bitcoind.rpcport" -> 28332, "eclair.bitcoind.zmqblock" -> "tcp://127.0.0.1:28334", "eclair.bitcoind.zmqtx" -> "tcp://127.0.0.1:28335", "eclair.mindepth-blocks" -> 2, "eclair.max-htlc-value-in-flight-msat" -> 100000000000L, "eclair.router-broadcast-interval" -> "2 second", "eclair.auto-reconnect" -> false))
instantiateEclairNode("A", ConfigFactory.parseMap(Map("eclair.node-alias" -> "A", "eclair.delay-blocks" -> 130, "eclair.server.port" -> 29730, "eclair.api.port" -> 28080, "eclair.channel-flags" -> 0)).withFallback(commonConfig)) // A's channels are private
instantiateEclairNode("B", ConfigFactory.parseMap(Map("eclair.node-alias" -> "B", "eclair.delay-blocks" -> 131, "eclair.server.port" -> 29731, "eclair.api.port" -> 28081)).withFallback(commonConfig))
instantiateEclairNode("C", ConfigFactory.parseMap(Map("eclair.node-alias" -> "C", "eclair.delay-blocks" -> 132, "eclair.server.port" -> 29732, "eclair.api.port" -> 28082, "eclair.payment-handler" -> "noop")).withFallback(commonConfig))
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala
index 347d86a4b..21ae51b30 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala
@@ -4,14 +4,15 @@ import java.net.InetSocketAddress
import akka.actor.ActorRef
import akka.testkit.TestProbe
+import fr.acinq.eclair.randomBytes
import fr.acinq.bitcoin.Crypto.PublicKey
import fr.acinq.eclair.TestConstants._
import fr.acinq.eclair.blockchain.EclairWallet
import fr.acinq.eclair.crypto.TransportHandler
-import fr.acinq.eclair.io.Peer.{CHANNELID_ZERO, ResumeAnnouncements}
+import fr.acinq.eclair.io.Peer.{CHANNELID_ZERO, ResumeAnnouncements, SendPing}
import fr.acinq.eclair.router.RoutingSyncSpec.makeFakeRoutingInfo
import fr.acinq.eclair.router.{ChannelRangeQueries, ChannelRangeQueriesSpec, Rebroadcast}
-import fr.acinq.eclair.wire.Error
+import fr.acinq.eclair.wire.{Error, Ping, Pong}
import fr.acinq.eclair.{ShortChannelId, TestkitBaseClass, wire}
import org.scalatest.Outcome
@@ -54,6 +55,39 @@ class PeerSpec extends TestkitBaseClass {
assert(probe.expectMsgType[Peer.PeerInfo].state == "CONNECTED")
}
+ test("reply to ping") { f =>
+ import f._
+ val probe = TestProbe()
+ connect(remoteNodeId, authenticator, watcher, router, relayer, connection, transport, peer)
+ val ping = Ping(42, randomBytes(127))
+ probe.send(peer, ping)
+ transport.expectMsg(TransportHandler.ReadAck(ping))
+ assert(transport.expectMsgType[Pong].data.size === ping.pongLength)
+ }
+
+ test("ignore malicious ping") { f =>
+ import f._
+ val probe = TestProbe()
+ connect(remoteNodeId, authenticator, watcher, router, relayer, connection, transport, peer)
+ // huge requested pong length
+ val ping = Ping(Int.MaxValue, randomBytes(127))
+ probe.send(peer, ping)
+ transport.expectMsg(TransportHandler.ReadAck(ping))
+ transport.expectNoMsg()
+ }
+
+ test("disconnect if no reply to ping") { f =>
+ import f._
+ val sender = TestProbe()
+ val deathWatcher = TestProbe()
+ connect(remoteNodeId, authenticator, watcher, router, relayer, connection, transport, peer)
+ // we manually trigger a ping because we don't want to wait too long in tests
+ sender.send(peer, SendPing)
+ transport.expectMsgType[Ping]
+ deathWatcher.watch(transport.ref)
+ deathWatcher.expectTerminated(transport.ref, max = 11 seconds)
+ }
+
test("filter gossip message (no filtering)") { f =>
import f._
val probe = TestProbe()
@@ -165,4 +199,5 @@ class PeerSpec extends TestkitBaseClass {
assert(error.channelId === CHANNELID_ZERO)
assert(new String(error.data).startsWith("bad announcement sig! bin=0100"))
}
+
}
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/HtlcGenerationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/HtlcGenerationSpec.scala
index fed98ada0..08b1c4030 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/HtlcGenerationSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/HtlcGenerationSpec.scala
@@ -99,7 +99,7 @@ class HtlcGenerationSpec extends FunSuite {
val (add, _) = buildCommand(finalAmountMsat, finalExpiry, paymentHash, hops)
assert(add.amountMsat > finalAmountMsat)
- assert(add.expiry === finalExpiry + channelUpdate_de.cltvExpiryDelta + channelUpdate_cd.cltvExpiryDelta + channelUpdate_bc.cltvExpiryDelta)
+ assert(add.cltvExpiry === finalExpiry + channelUpdate_de.cltvExpiryDelta + channelUpdate_cd.cltvExpiryDelta + channelUpdate_bc.cltvExpiryDelta)
assert(add.paymentHash === paymentHash)
assert(add.onion.length === Sphinx.PacketLength)
@@ -133,7 +133,7 @@ class HtlcGenerationSpec extends FunSuite {
val (add, _) = buildCommand(finalAmountMsat, finalExpiry, paymentHash, hops.take(1))
assert(add.amountMsat === finalAmountMsat)
- assert(add.expiry === finalExpiry)
+ assert(add.cltvExpiry === finalExpiry)
assert(add.paymentHash === paymentHash)
assert(add.onion.size === Sphinx.PacketLength)
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentHandlerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentHandlerSpec.scala
index 1ca28af3c..eb848066c 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentHandlerSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentHandlerSpec.scala
@@ -18,13 +18,14 @@ package fr.acinq.eclair.payment
import akka.actor.Status.Failure
import akka.actor.{ActorSystem, Status}
-import akka.testkit.{TestKit, TestProbe}
-import fr.acinq.bitcoin.{MilliSatoshi, Satoshi}
+import akka.testkit.{TestActorRef, TestKit, TestProbe}
+import fr.acinq.bitcoin.{BinaryData, MilliSatoshi, Satoshi}
import fr.acinq.eclair.TestConstants.Alice
import fr.acinq.eclair.channel.{CMD_FAIL_HTLC, CMD_FULFILL_HTLC}
+import fr.acinq.eclair.payment.LocalPaymentHandler.PendingPaymentRequest
import fr.acinq.eclair.payment.PaymentLifecycle.{CheckPayment, ReceivePayment}
import fr.acinq.eclair.payment.PaymentRequest.ExtraHop
-import fr.acinq.eclair.wire.{FinalExpiryTooSoon, UpdateAddHtlc}
+import fr.acinq.eclair.wire.{FinalExpiryTooSoon, UnknownPaymentHash, UpdateAddHtlc}
import fr.acinq.eclair.{Globals, ShortChannelId, randomKey}
import org.scalatest.FunSuiteLike
@@ -38,7 +39,7 @@ class PaymentHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLike
test("LocalPaymentHandler should reply with a fulfill/fail, emit a PaymentReceived and adds payment in DB") {
val nodeParams = Alice.nodeParams
- val handler = system.actorOf(LocalPaymentHandler.props(nodeParams))
+ val handler = TestActorRef[LocalPaymentHandler](LocalPaymentHandler.props(nodeParams))
val sender = TestProbe()
val eventListener = TestProbe()
system.eventStream.subscribe(eventListener.ref, classOf[PaymentReceived])
@@ -55,7 +56,7 @@ class PaymentHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLike
sender.send(handler, add)
sender.expectMsgType[CMD_FULFILL_HTLC]
val paymentRelayed = eventListener.expectMsgType[PaymentReceived]
- assert(paymentRelayed.copy(timestamp = 0) === PaymentReceived(amountMsat,add.paymentHash, add.channelId, timestamp = 0))
+ assert(paymentRelayed.copy(timestamp = 0) === PaymentReceived(amountMsat, add.paymentHash, add.channelId, timestamp = 0))
sender.send(handler, CheckPayment(pr.paymentHash))
assert(sender.expectMsgType[Boolean] === true)
}
@@ -69,7 +70,7 @@ class PaymentHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLike
sender.send(handler, add)
sender.expectMsgType[CMD_FULFILL_HTLC]
val paymentRelayed = eventListener.expectMsgType[PaymentReceived]
- assert(paymentRelayed.copy(timestamp = 0) === PaymentReceived(amountMsat,add.paymentHash, add.channelId, timestamp = 0))
+ assert(paymentRelayed.copy(timestamp = 0) === PaymentReceived(amountMsat, add.paymentHash, add.channelId, timestamp = 0))
sender.send(handler, CheckPayment(pr.paymentHash))
assert(sender.expectMsgType[Boolean] === true)
}
@@ -79,13 +80,36 @@ class PaymentHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLike
val pr = sender.expectMsgType[PaymentRequest]
sender.send(handler, CheckPayment(pr.paymentHash))
assert(sender.expectMsgType[Boolean] === false)
- val add = UpdateAddHtlc("11" * 32, 0, amountMsat.amount, pr.paymentHash, expiry = Globals.blockCount.get() + 3, "")
+ val add = UpdateAddHtlc("11" * 32, 0, amountMsat.amount, pr.paymentHash, cltvExpiry = Globals.blockCount.get() + 3, "")
sender.send(handler, add)
assert(sender.expectMsgType[CMD_FAIL_HTLC].reason == Right(FinalExpiryTooSoon))
eventListener.expectNoMsg(300 milliseconds)
sender.send(handler, CheckPayment(pr.paymentHash))
assert(sender.expectMsgType[Boolean] === false)
}
+ {
+ sender.send(handler, ReceivePayment(Some(amountMsat), "timeout expired", Some(1L)))
+ //allow request to timeout
+ Thread.sleep(1001)
+ val pr = sender.expectMsgType[PaymentRequest]
+ sender.send(handler, CheckPayment(pr.paymentHash))
+ assert(sender.expectMsgType[Boolean] === false)
+ val add = UpdateAddHtlc("11" * 32, 0, amountMsat.amount, pr.paymentHash, expiry, "")
+ sender.send(handler, add)
+ assert(sender.expectMsgType[CMD_FAIL_HTLC].reason == Right(UnknownPaymentHash))
+ // We chose UnknownPaymentHash on purpose. So if you have expired by 1 second or 1 hour you get the same error message.
+ eventListener.expectNoMsg(300 milliseconds)
+ sender.send(handler, CheckPayment(pr.paymentHash))
+ assert(sender.expectMsgType[Boolean] === false)
+ // make sure that the request is indeed pruned
+ sender.send(handler, 'requests)
+ sender.expectMsgType[Map[BinaryData, PendingPaymentRequest]].contains(pr.paymentHash)
+ sender.send(handler, LocalPaymentHandler.PurgeExpiredRequests)
+ awaitCond({
+ sender.send(handler, 'requests)
+ sender.expectMsgType[Map[BinaryData, PendingPaymentRequest]].contains(pr.paymentHash) == false
+ })
+ }
}
test("Payment request generation should fail when the amount asked in not valid") {
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/RelayerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/RelayerSpec.scala
index 7535d6163..3d2283928 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/RelayerSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/RelayerSpec.scala
@@ -66,7 +66,7 @@ class RelayerSpec extends TestkitBaseClass {
// we use this to build a valid onion
val (cmd, _) = buildCommand(finalAmountMsat, finalExpiry, paymentHash, hops)
// and then manually build an htlc
- val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.expiry, cmd.onion)
+ val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion)
relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc))
sender.send(relayer, ForwardAdd(add_ab))
@@ -86,7 +86,7 @@ class RelayerSpec extends TestkitBaseClass {
// we use this to build a valid onion
val (cmd, _) = buildCommand(finalAmountMsat, finalExpiry, paymentHash, hops)
// and then manually build an htlc
- val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.expiry, cmd.onion)
+ val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion)
sender.send(relayer, ForwardAdd(add_ab))
@@ -106,7 +106,7 @@ class RelayerSpec extends TestkitBaseClass {
// we use this to build a valid onion
val (cmd, _) = buildCommand(finalAmountMsat, finalExpiry, paymentHash, hops)
// and then manually build an htlc
- val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.expiry, cmd.onion)
+ val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion)
relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc))
sender.send(relayer, ForwardAdd(add_ab))
@@ -132,7 +132,7 @@ class RelayerSpec extends TestkitBaseClass {
// check that payments are sent properly
val (cmd, _) = buildCommand(finalAmountMsat, finalExpiry, paymentHash, hops)
- val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.expiry, cmd.onion)
+ val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion)
relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc))
sender.send(relayer, ForwardAdd(add_ab))
@@ -148,7 +148,7 @@ class RelayerSpec extends TestkitBaseClass {
relayer ! LocalChannelDown(sender.ref, channelId = channelId_bc, shortChannelId = channelUpdate_bc.shortChannelId, remoteNodeId = TestConstants.Bob.nodeParams.nodeId)
val (cmd1, _) = buildCommand(finalAmountMsat, finalExpiry, "02" * 32, hops)
- val add_ab1 = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd1.amountMsat, cmd1.paymentHash, cmd1.expiry, cmd1.onion)
+ val add_ab1 = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd1.amountMsat, cmd1.paymentHash, cmd1.cltvExpiry, cmd1.onion)
sender.send(relayer, ForwardAdd(add_ab))
val fail = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message
@@ -166,7 +166,7 @@ class RelayerSpec extends TestkitBaseClass {
// we use this to build a valid onion
val (cmd, _) = buildCommand(finalAmountMsat, finalExpiry, paymentHash, hops)
// and then manually build an htlc
- val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.expiry, cmd.onion)
+ val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion)
val channelUpdate_bc_disabled = channelUpdate_bc.copy(channelFlags = Announcements.makeChannelFlags(Announcements.isNode1(channelUpdate_bc.channelFlags), enable = false))
relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc_disabled, makeCommitments(channelId_bc))
@@ -187,7 +187,7 @@ class RelayerSpec extends TestkitBaseClass {
// we use this to build a valid onion
val (cmd, _) = buildCommand(finalAmountMsat, finalExpiry, paymentHash, hops)
// and then manually build an htlc
- val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.expiry, "00" * Sphinx.PacketLength)
+ val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, "00" * Sphinx.PacketLength)
relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc))
sender.send(relayer, ForwardAdd(add_ab))
@@ -207,7 +207,7 @@ class RelayerSpec extends TestkitBaseClass {
// we use this to build a valid onion
val (cmd, _) = buildCommand(channelUpdate_bc.htlcMinimumMsat - 1, finalExpiry, paymentHash, hops.map(hop => hop.copy(lastUpdate = hop.lastUpdate.copy(feeBaseMsat = 0, feeProportionalMillionths = 0))))
// and then manually build an htlc
- val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.expiry, cmd.onion)
+ val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion)
relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc))
sender.send(relayer, ForwardAdd(add_ab))
@@ -227,14 +227,14 @@ class RelayerSpec extends TestkitBaseClass {
val hops1 = hops.updated(1, hops(1).copy(lastUpdate = hops(1).lastUpdate.copy(cltvExpiryDelta = 0)))
val (cmd, _) = buildCommand(finalAmountMsat, finalExpiry, paymentHash, hops1)
// and then manually build an htlc
- val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.expiry, cmd.onion)
+ val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion)
relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc))
sender.send(relayer, ForwardAdd(add_ab))
val fail = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message
assert(fail.id === add_ab.id)
- assert(fail.reason == Right(IncorrectCltvExpiry(cmd.expiry, channelUpdate_bc)))
+ assert(fail.reason == Right(IncorrectCltvExpiry(cmd.cltvExpiry, channelUpdate_bc)))
register.expectNoMsg(100 millis)
paymentHandler.expectNoMsg(100 millis)
@@ -247,7 +247,7 @@ class RelayerSpec extends TestkitBaseClass {
val hops1 = hops.updated(1, hops(1).copy(lastUpdate = hops(1).lastUpdate.copy(feeBaseMsat = hops(1).lastUpdate.feeBaseMsat / 2)))
val (cmd, _) = buildCommand(finalAmountMsat, finalExpiry, paymentHash, hops1)
// and then manually build an htlc
- val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.expiry, cmd.onion)
+ val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion)
relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc))
sender.send(relayer, ForwardAdd(add_ab))
@@ -268,7 +268,7 @@ class RelayerSpec extends TestkitBaseClass {
val hops1 = hops.head :: Nil
val (cmd, _) = buildCommand(finalAmountMsat, finalExpiry, paymentHash, hops1)
// and then manually build an htlc with a wrong expiry
- val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat - 1, cmd.paymentHash, cmd.expiry, cmd.onion)
+ val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat - 1, cmd.paymentHash, cmd.cltvExpiry, cmd.onion)
relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc))
sender.send(relayer, ForwardAdd(add_ab))
@@ -289,14 +289,14 @@ class RelayerSpec extends TestkitBaseClass {
val hops1 = hops.head :: Nil
val (cmd, _) = buildCommand(finalAmountMsat, finalExpiry, paymentHash, hops1)
// and then manually build an htlc with a wrong expiry
- val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.expiry - 1, cmd.onion)
+ val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry - 1, cmd.onion)
relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc))
sender.send(relayer, ForwardAdd(add_ab))
val fail = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message
assert(fail.id === add_ab.id)
- assert(fail.reason == Right(FinalIncorrectCltvExpiry(add_ab.expiry)))
+ assert(fail.reason == Right(FinalIncorrectCltvExpiry(add_ab.cltvExpiry)))
register.expectNoMsg(100 millis)
paymentHandler.expectNoMsg(100 millis)
@@ -340,7 +340,7 @@ class RelayerSpec extends TestkitBaseClass {
system.eventStream.subscribe(eventListener.ref, classOf[PaymentEvent])
// we build a fake htlc for the downstream channel
- val add_bc = UpdateAddHtlc(channelId = channelId_bc, id = 72, amountMsat = 10000000L, paymentHash = "00" * 32, expiry = 4200, onionRoutingPacket = "")
+ val add_bc = UpdateAddHtlc(channelId = channelId_bc, id = 72, amountMsat = 10000000L, paymentHash = "00" * 32, cltvExpiry = 4200, onionRoutingPacket = "")
val fulfill_ba = UpdateFulfillHtlc(channelId = channelId_bc, id = 42, paymentPreimage = "00" * 32)
val origin = Relayed(channelId_ab, 150, 11000000L, 10000000L)
sender.send(relayer, ForwardFulfill(fulfill_ba, origin, add_bc))
@@ -358,7 +358,7 @@ class RelayerSpec extends TestkitBaseClass {
val sender = TestProbe()
// we build a fake htlc for the downstream channel
- val add_bc = UpdateAddHtlc(channelId = channelId_bc, id = 72, amountMsat = 10000000L, paymentHash = "00" * 32, expiry = 4200, onionRoutingPacket = "")
+ val add_bc = UpdateAddHtlc(channelId = channelId_bc, id = 72, amountMsat = 10000000L, paymentHash = "00" * 32, cltvExpiry = 4200, onionRoutingPacket = "")
val fail_ba = UpdateFailHtlc(channelId = channelId_bc, id = 42, reason = Sphinx.createErrorPacket(BinaryData("01" * 32), TemporaryChannelFailure(channelUpdate_cd)))
val origin = Relayed(channelId_ab, 150, 11000000L, 10000000L)
sender.send(relayer, ForwardFail(fail_ba, origin, add_bc))
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsBatchValidationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsBatchValidationSpec.scala
index 9e96a810d..3ebaabdf8 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsBatchValidationSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsBatchValidationSpec.scala
@@ -19,6 +19,7 @@ package fr.acinq.eclair.router
import akka.actor.ActorSystem
import akka.pattern.pipe
import akka.testkit.TestProbe
+import com.softwaremill.sttp.okhttp.OkHttpFutureBackend
import fr.acinq.bitcoin.Crypto.PrivateKey
import fr.acinq.bitcoin.{BinaryData, Block, Satoshi, Script, Transaction}
import fr.acinq.eclair.blockchain.ValidateResult
@@ -44,6 +45,7 @@ class AnnouncementsBatchValidationSpec extends FunSuite {
import scala.concurrent.ExecutionContext.Implicits.global
implicit val system = ActorSystem()
+ implicit val sttpBackend = OkHttpFutureBackend()
implicit val extendedBitcoinClient = new ExtendedBitcoinClient(new BasicBitcoinJsonRPCClient(user = "foo", password = "bar", host = "localhost", port = 18332))
val channels = for (i <- 0 until 50) yield {
@@ -76,7 +78,7 @@ object AnnouncementsBatchValidationSpec {
def generateBlocks(numBlocks: Int)(implicit extendedBitcoinClient: ExtendedBitcoinClient, ec: ExecutionContext) =
Await.result(extendedBitcoinClient.rpcClient.invoke("generate", numBlocks), 10 seconds)
- def simulateChannel()(implicit extendedBitcoinClient: ExtendedBitcoinClient, ec: ExecutionContext, system: ActorSystem): SimulatedChannel = {
+ def simulateChannel()(implicit extendedBitcoinClient: ExtendedBitcoinClient, ec: ExecutionContext): SimulatedChannel = {
val node1Key = randomKey
val node2Key = randomKey
val node1BitcoinKey = randomKey
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TestVectorsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TestVectorsSpec.scala
index 1bf7e63e5..2989ca04b 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TestVectorsSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TestVectorsSpec.scala
@@ -160,7 +160,7 @@ class TestVectorsSpec extends FunSuite with Logging {
)
val htlcScripts = htlcs.map(htlc => htlc.direction match {
case OUT => Scripts.htlcOffered(Local.payment_privkey.publicKey, Remote.payment_privkey.publicKey, Local.revocation_pubkey, Crypto.ripemd160(htlc.add.paymentHash))
- case IN => Scripts.htlcReceived(Local.payment_privkey.publicKey, Remote.payment_privkey.publicKey, Local.revocation_pubkey, Crypto.ripemd160(htlc.add.paymentHash), htlc.add.expiry)
+ case IN => Scripts.htlcReceived(Local.payment_privkey.publicKey, Remote.payment_privkey.publicKey, Local.revocation_pubkey, Crypto.ripemd160(htlc.add.paymentHash), htlc.add.cltvExpiry)
})
def dir2string(dir: Direction) = dir match {
@@ -171,7 +171,7 @@ class TestVectorsSpec extends FunSuite with Logging {
for (i <- 0 until htlcs.length) {
logger.info(s"htlc $i direction: ${dir2string(htlcs(i).direction)}")
logger.info(s"htlc $i amount_msat: ${htlcs(i).add.amountMsat}")
- logger.info(s"htlc $i expiry: ${htlcs(i).add.expiry}")
+ logger.info(s"htlc $i expiry: ${htlcs(i).add.cltvExpiry}")
logger.info(s"htlc $i payment_preimage: ${paymentPreimages(i)}")
}
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala
index e8f2d537e..7c4f05470 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala
@@ -124,8 +124,8 @@ class TransactionsSpec extends FunSuite with Logging {
// HtlcPenaltyTx
// first we create a fake commitTx tx, containing only the output that will be spent by the ClaimHtlcSuccessTx
val paymentPreimage = BinaryData("42" * 32)
- val htlc = UpdateAddHtlc("00" * 32, 0, Satoshi(20000).amount * 1000, sha256(paymentPreimage), expiry = 400144, BinaryData.empty)
- val redeemScript = htlcReceived(localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localRevocationPriv.publicKey, ripemd160(htlc.paymentHash), htlc.expiry)
+ val htlc = UpdateAddHtlc("00" * 32, 0, Satoshi(20000).amount * 1000, sha256(paymentPreimage), cltvExpiry = 400144, BinaryData.empty)
+ val redeemScript = htlcReceived(localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localRevocationPriv.publicKey, ripemd160(htlc.paymentHash), htlc.cltvExpiry)
val pubKeyScript = write(pay2wsh(redeemScript))
val commitTx = Transaction(version = 0, txIn = Nil, txOut = TxOut(Satoshi(htlc.amountMsat / 1000), pubKeyScript) :: Nil, lockTime = 0)
val htlcPenaltyTx = makeHtlcPenaltyTx(commitTx, outputsAlreadyUsed = Set.empty, Script.write(redeemScript), localDustLimit, finalPubKeyScript, feeratePerKw)
@@ -139,7 +139,7 @@ class TransactionsSpec extends FunSuite with Logging {
// ClaimHtlcSuccessTx
// first we create a fake commitTx tx, containing only the output that will be spent by the ClaimHtlcSuccessTx
val paymentPreimage = BinaryData("42" * 32)
- val htlc = UpdateAddHtlc("00" * 32, 0, Satoshi(20000).amount * 1000, sha256(paymentPreimage), expiry = 400144, BinaryData.empty)
+ val htlc = UpdateAddHtlc("00" * 32, 0, Satoshi(20000).amount * 1000, sha256(paymentPreimage), cltvExpiry = 400144, BinaryData.empty)
val pubKeyScript = write(pay2wsh(htlcOffered(localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localRevocationPriv.publicKey, ripemd160(htlc.paymentHash))))
val commitTx = Transaction(version = 0, txIn = Nil, txOut = TxOut(Satoshi(htlc.amountMsat / 1000), pubKeyScript) :: Nil, lockTime = 0)
val claimHtlcSuccessTx = makeClaimHtlcSuccessTx(commitTx, outputsAlreadyUsed = Set.empty, localDustLimit, remoteHtlcPriv.publicKey, localHtlcPriv.publicKey, localRevocationPriv.publicKey, finalPubKeyScript, htlc, feeratePerKw)
@@ -153,8 +153,8 @@ class TransactionsSpec extends FunSuite with Logging {
// ClaimHtlcTimeoutTx
// first we create a fake commitTx tx, containing only the output that will be spent by the ClaimHtlcSuccessTx
val paymentPreimage = BinaryData("42" * 32)
- val htlc = UpdateAddHtlc("00" * 32, 0, Satoshi(20000).amount * 1000, sha256(paymentPreimage), expiry = 400144, BinaryData.empty)
- val pubKeyScript = write(pay2wsh(htlcReceived(localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localRevocationPriv.publicKey, ripemd160(htlc.paymentHash), htlc.expiry)))
+ val htlc = UpdateAddHtlc("00" * 32, 0, Satoshi(20000).amount * 1000, sha256(paymentPreimage), cltvExpiry = 400144, BinaryData.empty)
+ val pubKeyScript = write(pay2wsh(htlcReceived(localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localRevocationPriv.publicKey, ripemd160(htlc.paymentHash), htlc.cltvExpiry)))
val commitTx = Transaction(version = 0, txIn = Nil, txOut = TxOut(Satoshi(htlc.amountMsat / 1000), pubKeyScript) :: Nil, lockTime = 0)
val claimClaimHtlcTimeoutTx = makeClaimHtlcTimeoutTx(commitTx, outputsAlreadyUsed = Set.empty, localDustLimit, remoteHtlcPriv.publicKey, localHtlcPriv.publicKey, localRevocationPriv.publicKey, finalPubKeyScript, htlc, feeratePerKw)
// we use dummy signatures to compute the weight
@@ -304,7 +304,7 @@ class TransactionsSpec extends FunSuite with Logging {
{
// remote spends received HTLC output with revocation key
- val script = Script.write(Scripts.htlcReceived(localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localRevocationPriv.publicKey, Crypto.ripemd160(htlc2.paymentHash), htlc2.expiry))
+ val script = Script.write(Scripts.htlcReceived(localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localRevocationPriv.publicKey, Crypto.ripemd160(htlc2.paymentHash), htlc2.cltvExpiry))
val htlcPenaltyTx = makeHtlcPenaltyTx(commitTx.tx, outputsAlreadyUsed = Set.empty, script, localDustLimit, finalPubKeyScript, feeratePerKw)
val sig = sign(htlcPenaltyTx, localRevocationPriv)
val signed = addSigs(htlcPenaltyTx, sig, localRevocationPriv.publicKey)
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/ChannelCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/ChannelCodecsSpec.scala
index 40d328c3e..87e0c8e41 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/ChannelCodecsSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/ChannelCodecsSpec.scala
@@ -104,7 +104,7 @@ class ChannelCodecsSpec extends FunSuite {
channelId = randomBytes(32),
id = Random.nextInt(Int.MaxValue),
amountMsat = Random.nextInt(Int.MaxValue),
- expiry = Random.nextInt(Int.MaxValue),
+ cltvExpiry = Random.nextInt(Int.MaxValue),
paymentHash = randomBytes(32),
onionRoutingPacket = randomBytes(Sphinx.PacketLength))
val htlc1 = DirectedHtlc(direction = IN, add = add)
@@ -118,14 +118,14 @@ class ChannelCodecsSpec extends FunSuite {
channelId = randomBytes(32),
id = Random.nextInt(Int.MaxValue),
amountMsat = Random.nextInt(Int.MaxValue),
- expiry = Random.nextInt(Int.MaxValue),
+ cltvExpiry = Random.nextInt(Int.MaxValue),
paymentHash = randomBytes(32),
onionRoutingPacket = randomBytes(Sphinx.PacketLength))
val add2 = UpdateAddHtlc(
channelId = randomBytes(32),
id = Random.nextInt(Int.MaxValue),
amountMsat = Random.nextInt(Int.MaxValue),
- expiry = Random.nextInt(Int.MaxValue),
+ cltvExpiry = Random.nextInt(Int.MaxValue),
paymentHash = randomBytes(32),
onionRoutingPacket = randomBytes(Sphinx.PacketLength))
val htlc1 = DirectedHtlc(direction = IN, add = add1)
diff --git a/pom.xml b/pom.xml
index 8037b6665..720c1ab79 100644
--- a/pom.xml
+++ b/pom.xml
@@ -65,6 +65,7 @@
2.11.11
2.11
2.3.14
+ 1.3.9
0.9.17
24.0-android