From 2fbf46a3449793ac4e4fdaa0107aa4e3b39afee0 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Padiou Date: Wed, 11 Sep 2019 10:55:43 +0200 Subject: [PATCH] Removed Globals class (#1127) This is a prerequisite to parallelization of tests. --- .../scala/fr/acinq/eclair/CltvExpiry.scala | 2 +- .../main/scala/fr/acinq/eclair/Eclair.scala | 2 +- .../main/scala/fr/acinq/eclair/Globals.scala | 48 ------- .../scala/fr/acinq/eclair/NodeParams.scala | 6 +- .../main/scala/fr/acinq/eclair/Setup.scala | 44 +++++-- .../blockchain/bitcoind/ZmqWatcher.scala | 12 +- .../electrum/ElectrumClientPool.scala | 8 +- .../blockchain/electrum/ElectrumWatcher.scala | 52 ++------ .../fr/acinq/eclair/channel/Channel.scala | 2 +- .../fr/acinq/eclair/channel/Commitments.scala | 13 +- .../eclair/payment/LocalPaymentHandler.scala | 15 ++- .../eclair/payment/PaymentInitiator.scala | 2 +- .../scala/fr/acinq/eclair/router/Router.scala | 17 ++- .../fr/acinq/eclair/CltvExpirySpec.scala | 6 +- .../scala/fr/acinq/eclair/StartupSpec.scala | 7 +- .../scala/fr/acinq/eclair/TestConstants.scala | 3 + .../scala/fr/acinq/eclair/TestUtils.scala | 15 +++ .../fr/acinq/eclair/TestkitBaseClass.scala | 4 - .../bitcoind/BitcoinCoreWalletSpec.scala | 4 +- .../blockchain/bitcoind/BitcoindService.scala | 39 +++++- .../bitcoind/ExtendedBitcoinClientSpec.scala | 4 +- .../electrum/ElectrumClientPoolSpec.scala | 3 +- .../electrum/ElectrumWalletSpec.scala | 3 +- .../electrum/ElectrumWatcherSpec.scala | 16 ++- .../electrum/ElectrumxService.scala | 10 +- .../fee/BitcoinCoreFeeProviderSpec.scala | 4 +- .../eclair/channel/CommitmentsSpec.scala | 20 +-- .../fr/acinq/eclair/channel/FuzzySpec.scala | 122 ++++++++---------- .../acinq/eclair/channel/ThroughputSpec.scala | 3 +- .../states/StateTestsHelperMethods.scala | 11 +- .../channel/states/e/NormalStateSpec.scala | 98 +++++++------- .../channel/states/e/OfflineStateSpec.scala | 4 +- .../channel/states/f/ShutdownStateSpec.scala | 4 +- .../eclair/integration/IntegrationSpec.scala | 50 +++---- .../interop/rustytests/RustyTestsSpec.scala | 11 +- .../eclair/payment/HtlcGenerationSpec.scala | 6 +- .../eclair/payment/PaymentHandlerSpec.scala | 10 +- .../eclair/payment/PaymentLifecycleSpec.scala | 24 ++-- .../eclair/router/RouteCalculationSpec.scala | 101 +++++++-------- .../fr/acinq/eclair/router/RouterSpec.scala | 4 +- .../acinq/eclair/router/RoutingSyncSpec.scala | 1 + .../transactions/TransactionsSpec.scala | 7 +- .../scala/fr/acinq/eclair/gui/Handlers.scala | 14 ++ .../controllers/OpenChannelController.scala | 14 +- pom.xml | 3 +- 45 files changed, 429 insertions(+), 419 deletions(-) delete mode 100644 eclair-core/src/main/scala/fr/acinq/eclair/Globals.scala diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/CltvExpiry.scala b/eclair-core/src/main/scala/fr/acinq/eclair/CltvExpiry.scala index 3452df377c..d3249fd461 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/CltvExpiry.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/CltvExpiry.scala @@ -49,7 +49,7 @@ case class CltvExpiryDelta(private val underlying: Int) extends Ordered[CltvExpi /** * Adds the current block height to the given delta to obtain an absolute expiry. */ - def toCltvExpiry = CltvExpiry(Globals.blockCount.get() + underlying) + def toCltvExpiry(blockHeight: Long) = CltvExpiry(blockHeight + underlying) // @formatter:off def +(other: Int): CltvExpiryDelta = CltvExpiryDelta(underlying + other) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala index 7b485e0461..fc8e7356c5 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala @@ -272,7 +272,7 @@ class EclairImpl(appKit: Kit) extends Eclair { GetInfoResponse(nodeId = appKit.nodeParams.nodeId, alias = appKit.nodeParams.alias, chainHash = appKit.nodeParams.chainHash, - blockHeight = Globals.blockCount.intValue(), + blockHeight = appKit.nodeParams.currentBlockHeight.toInt, publicAddresses = appKit.nodeParams.publicAddresses) ) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Globals.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Globals.scala deleted file mode 100644 index 5cc3630947..0000000000 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Globals.scala +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2019 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 java.util.concurrent.atomic.{AtomicLong, AtomicReference} - -import fr.acinq.eclair.blockchain.fee.{FeeratesPerKB, FeeratesPerKw} - -/** - * Created by PM on 25/01/2016. - */ -object Globals { - - /** - * This counter holds the current blockchain height. - * It is mainly used to calculate htlc expiries. - * The value is read by all actors, hence it needs to be thread-safe. - */ - val blockCount = new AtomicLong(0) - - /** - * This holds the current feerates, in satoshi-per-kilobytes. - * The value is read by all actors, hence it needs to be thread-safe. - */ - val feeratesPerKB = new AtomicReference[FeeratesPerKB](null) - - /** - * This holds the current feerates, in satoshi-per-kw. - * The value is read by all actors, hence it needs to be thread-safe. - */ - val feeratesPerKw = new AtomicReference[FeeratesPerKw](null) -} - - diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala b/eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala index ce3f9fc2da..6db6b030bb 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala @@ -20,6 +20,7 @@ import java.io.File import java.net.InetSocketAddress import java.nio.file.Files import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicLong import com.typesafe.config.{Config, ConfigFactory} import fr.acinq.bitcoin.Crypto.PublicKey @@ -41,6 +42,7 @@ import scala.concurrent.duration.FiniteDuration * Created by PM on 26/02/2017. */ case class NodeParams(keyManager: KeyManager, + private val blockCount: AtomicLong, alias: String, color: Color, publicAddresses: List[NodeAddress], @@ -80,6 +82,7 @@ case class NodeParams(keyManager: KeyManager, maxPaymentAttempts: Int) { val privateKey = keyManager.nodeKey.privateKey val nodeId = keyManager.nodeId + def currentBlockHeight: Long = blockCount.get } object NodeParams { @@ -124,7 +127,7 @@ object NodeParams { } } - def makeNodeParams(config: Config, keyManager: KeyManager, torAddress_opt: Option[NodeAddress], database: Databases, feeEstimator: FeeEstimator): NodeParams = { + def makeNodeParams(config: Config, keyManager: KeyManager, torAddress_opt: Option[NodeAddress], database: Databases, blockCount: AtomicLong, feeEstimator: FeeEstimator): NodeParams = { val chain = config.getString("chain") val chainHash = makeChainHash(chain) @@ -201,6 +204,7 @@ object NodeParams { NodeParams( keyManager = keyManager, + blockCount = blockCount, alias = nodeAlias, color = Color(color(0), color(1), color(2)), publicAddresses = addresses, 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 5979599b02..5d4f0a1861 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 java.net.InetSocketAddress import java.sql.DriverManager import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.{AtomicLong, AtomicReference} import akka.Done import akka.actor.{ActorRef, ActorSystem, Props, SupervisorStrategy} @@ -93,12 +94,31 @@ class Setup(datadir: File, case None => Databases.sqliteJDBC(chaindir) } + /** + * This counter holds the current blockchain height. + * It is mainly used to calculate htlc expiries. + * The value is read by all actors, hence it needs to be thread-safe. + */ + val blockCount = new AtomicLong(0) + + /** + * This holds the current feerates, in satoshi-per-kilobytes. + * The value is read by all actors, hence it needs to be thread-safe. + */ + val feeratesPerKB = new AtomicReference[FeeratesPerKB](null) + + /** + * This holds the current feerates, in satoshi-per-kw. + * The value is read by all actors, hence it needs to be thread-safe. + */ + val feeratesPerKw = new AtomicReference[FeeratesPerKw](null) + val feeEstimator = new FeeEstimator { - override def getFeeratePerKb(target: Int): Long = Globals.feeratesPerKB.get().feePerBlock(target) - override def getFeeratePerKw(target: Int): Long = Globals.feeratesPerKw.get().feePerBlock(target) + override def getFeeratePerKb(target: Int): Long = feeratesPerKB.get().feePerBlock(target) + override def getFeeratePerKw(target: Int): Long = feeratesPerKw.get().feePerBlock(target) } - val nodeParams = NodeParams.makeNodeParams(config, keyManager, initTor(), database, feeEstimator) + val nodeParams = NodeParams.makeNodeParams(config, keyManager, initTor(), database, blockCount, feeEstimator) val serverBindingAddress = new InetSocketAddress( config.getString("server.binding-ip"), @@ -170,7 +190,7 @@ class Setup(datadir: File, val stream = classOf[Setup].getResourceAsStream(addressesFile) ElectrumClientPool.readServerAddresses(stream, sslEnabled) } - val electrumClient = system.actorOf(SimpleSupervisor.props(Props(new ElectrumClientPool(addresses)), "electrum-client", SupervisorStrategy.Resume)) + val electrumClient = system.actorOf(SimpleSupervisor.props(Props(new ElectrumClientPool(blockCount, addresses)), "electrum-client", SupervisorStrategy.Resume)) Electrum(electrumClient) } @@ -193,8 +213,8 @@ class Setup(datadir: File, blocks_72 = config.getLong("on-chain-fees.default-feerates.72"), blocks_144 = config.getLong("on-chain-fees.default-feerates.144") ) - Globals.feeratesPerKB.set(confDefaultFeerates) - Globals.feeratesPerKw.set(FeeratesPerKw(confDefaultFeerates)) + feeratesPerKB.set(confDefaultFeerates) + feeratesPerKw.set(FeeratesPerKw(confDefaultFeerates)) confDefaultFeerates } minFeeratePerByte = config.getLong("min-feerate") @@ -208,10 +228,10 @@ class Setup(datadir: File, } _ = system.scheduler.schedule(0 seconds, 10 minutes)(feeProvider.getFeerates.map { case feerates: FeeratesPerKB => - Globals.feeratesPerKB.set(feerates) - Globals.feeratesPerKw.set(FeeratesPerKw(feerates)) - system.eventStream.publish(CurrentFeerates(Globals.feeratesPerKw.get)) - logger.info(s"current feeratesPerKB=${Globals.feeratesPerKB.get()} feeratesPerKw=${Globals.feeratesPerKw.get()}") + feeratesPerKB.set(feerates) + feeratesPerKw.set(FeeratesPerKw(feerates)) + system.eventStream.publish(CurrentFeerates(feeratesPerKw.get)) + logger.info(s"current feeratesPerKB=${feeratesPerKB.get()} feeratesPerKw=${feeratesPerKw.get()}") feeratesRetrieved.trySuccess(Done) }) _ <- feeratesRetrieved.future @@ -220,11 +240,11 @@ class Setup(datadir: File, case Bitcoind(bitcoinClient) => system.actorOf(SimpleSupervisor.props(Props(new ZMQActor(config.getString("bitcoind.zmqblock"), Some(zmqBlockConnected))), "zmqblock", SupervisorStrategy.Restart)) system.actorOf(SimpleSupervisor.props(Props(new ZMQActor(config.getString("bitcoind.zmqtx"), Some(zmqTxConnected))), "zmqtx", SupervisorStrategy.Restart)) - system.actorOf(SimpleSupervisor.props(ZmqWatcher.props(new ExtendedBitcoinClient(new BatchingBitcoinJsonRPCClient(bitcoinClient))), "watcher", SupervisorStrategy.Resume)) + system.actorOf(SimpleSupervisor.props(ZmqWatcher.props(blockCount, new ExtendedBitcoinClient(new BatchingBitcoinJsonRPCClient(bitcoinClient))), "watcher", SupervisorStrategy.Resume)) case Electrum(electrumClient) => zmqBlockConnected.success(Done) zmqTxConnected.success(Done) - system.actorOf(SimpleSupervisor.props(Props(new ElectrumWatcher(electrumClient)), "watcher", SupervisorStrategy.Resume)) + system.actorOf(SimpleSupervisor.props(Props(new ElectrumWatcher(blockCount, electrumClient)), "watcher", SupervisorStrategy.Resume)) } router = system.actorOf(SimpleSupervisor.props(Router.props(nodeParams, watcher, Some(routerInitialized)), "router", SupervisorStrategy.Resume)) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/ZmqWatcher.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/ZmqWatcher.scala index b6feb4afff..73511db293 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/ZmqWatcher.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/ZmqWatcher.scala @@ -17,11 +17,11 @@ package fr.acinq.eclair.blockchain.bitcoind import java.util.concurrent.Executors +import java.util.concurrent.atomic.AtomicLong import akka.actor.{Actor, ActorLogging, Cancellable, Props, Terminated} import akka.pattern.pipe import fr.acinq.bitcoin._ -import fr.acinq.eclair.Globals import fr.acinq.eclair.blockchain._ import fr.acinq.eclair.blockchain.bitcoind.rpc.ExtendedBitcoinClient import fr.acinq.eclair.channel.BITCOIN_PARENT_TX_CONFIRMED @@ -39,7 +39,7 @@ import scala.util.Try * - also uses bitcoin-core rpc api, most notably for tx confirmation count and blockcount (because reorgs) * Created by PM on 21/02/2016. */ -class ZmqWatcher(client: ExtendedBitcoinClient)(implicit ec: ExecutionContext = ExecutionContext.global) extends Actor with ActorLogging { +class ZmqWatcher(blockCount: AtomicLong, client: ExtendedBitcoinClient)(implicit ec: ExecutionContext = ExecutionContext.global) extends Actor with ActorLogging { import ZmqWatcher._ @@ -80,7 +80,7 @@ class ZmqWatcher(client: ExtendedBitcoinClient)(implicit ec: ExecutionContext = client.getBlockCount.map { case count => log.debug(s"setting blockCount=$count") - Globals.blockCount.set(count) + blockCount.set(count) context.system.eventStream.publish(CurrentBlockCount(count)) } // TODO: beware of the herd effect @@ -151,7 +151,7 @@ class ZmqWatcher(client: ExtendedBitcoinClient)(implicit ec: ExecutionContext = context become watching(watches + w, addWatchedUtxos(watchedUtxos, w), block2tx, nextTick) case PublishAsap(tx) => - val blockCount = Globals.blockCount.get() + val blockCount = this.blockCount.get() val cltvTimeout = Scripts.cltvTimeout(tx) val csvTimeout = Scripts.csvTimeout(tx) if (csvTimeout > 0) { @@ -168,7 +168,7 @@ class ZmqWatcher(client: ExtendedBitcoinClient)(implicit ec: ExecutionContext = case WatchEventConfirmed(BITCOIN_PARENT_TX_CONFIRMED(tx), blockHeight, _, _) => log.info(s"parent tx of txid=${tx.txid} has been confirmed") - val blockCount = Globals.blockCount.get() + val blockCount = this.blockCount.get() val csvTimeout = Scripts.csvTimeout(tx) val absTimeout = blockHeight + csvTimeout if (absTimeout > blockCount) { @@ -226,7 +226,7 @@ class ZmqWatcher(client: ExtendedBitcoinClient)(implicit ec: ExecutionContext = object ZmqWatcher { - def props(client: ExtendedBitcoinClient)(implicit ec: ExecutionContext = ExecutionContext.global) = Props(new ZmqWatcher(client)(ec)) + def props(blockCount: AtomicLong, client: ExtendedBitcoinClient)(implicit ec: ExecutionContext = ExecutionContext.global) = Props(new ZmqWatcher(blockCount, client)(ec)) case object TickNewBlock diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/electrum/ElectrumClientPool.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/electrum/ElectrumClientPool.scala index b7249d409a..117b204bf1 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/electrum/ElectrumClientPool.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/electrum/ElectrumClientPool.scala @@ -18,10 +18,10 @@ package fr.acinq.eclair.blockchain.electrum import java.io.InputStream import java.net.InetSocketAddress +import java.util.concurrent.atomic.AtomicLong import akka.actor.{Actor, ActorRef, FSM, OneForOneStrategy, Props, SupervisorStrategy, Terminated} import fr.acinq.bitcoin.BlockHeader -import fr.acinq.eclair.Globals import fr.acinq.eclair.blockchain.CurrentBlockCount import fr.acinq.eclair.blockchain.electrum.ElectrumClient.SSL import fr.acinq.eclair.blockchain.electrum.ElectrumClientPool.ElectrumServerAddress @@ -32,7 +32,7 @@ import scala.concurrent.ExecutionContext import scala.concurrent.duration._ import scala.util.Random -class ElectrumClientPool(serverAddresses: Set[ElectrumServerAddress])(implicit val ec: ExecutionContext) extends Actor with FSM[ElectrumClientPool.State, ElectrumClientPool.Data] { +class ElectrumClientPool(blockCount: AtomicLong, serverAddresses: Set[ElectrumServerAddress])(implicit val ec: ExecutionContext) extends Actor with FSM[ElectrumClientPool.State, ElectrumClientPool.Data] { import ElectrumClientPool._ val statusListeners = collection.mutable.HashSet.empty[ActorRef] @@ -166,10 +166,10 @@ class ElectrumClientPool(serverAddresses: Set[ElectrumServerAddress])(implicit v private def updateBlockCount(blockCount: Long): Unit = { // when synchronizing we don't want to advertise previous blocks - if (Globals.blockCount.get() < blockCount) { + if (this.blockCount.get() < blockCount) { log.debug("current blockchain height={}", blockCount) context.system.eventStream.publish(CurrentBlockCount(blockCount)) - Globals.blockCount.set(blockCount) + this.blockCount.set(blockCount) } } } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/electrum/ElectrumWatcher.scala b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/electrum/ElectrumWatcher.scala index be45eb2b96..48aab4bbe3 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/electrum/ElectrumWatcher.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/blockchain/electrum/ElectrumWatcher.scala @@ -16,20 +16,21 @@ package fr.acinq.eclair.blockchain.electrum -import java.net.InetSocketAddress +import java.util.concurrent.atomic.AtomicLong -import akka.actor.{Actor, ActorLogging, ActorRef, ActorSystem, Props, Stash, Terminated} +import akka.actor.{Actor, ActorLogging, ActorRef, Stash, Terminated} import fr.acinq.bitcoin.{BlockHeader, ByteVector32, Script, Transaction, TxIn, TxOut} import fr.acinq.eclair.blockchain._ -import fr.acinq.eclair.blockchain.electrum.ElectrumClient.{SSL, computeScriptHash} -import fr.acinq.eclair.channel.{BITCOIN_FUNDING_DEPTHOK, BITCOIN_FUNDING_SPENT, BITCOIN_PARENT_TX_CONFIRMED} +import fr.acinq.eclair.blockchain.electrum.ElectrumClient.computeScriptHash +import fr.acinq.eclair.channel.BITCOIN_PARENT_TX_CONFIRMED import fr.acinq.eclair.transactions.Scripts -import fr.acinq.eclair.{Globals, LongToBtcAmount, ShortChannelId, TxCoordinates} +import fr.acinq.eclair.{LongToBtcAmount, ShortChannelId, TxCoordinates} import scala.collection.SortedMap import scala.collection.immutable.Queue -class ElectrumWatcher(client: ActorRef) extends Actor with Stash with ActorLogging { + +class ElectrumWatcher(blockCount: AtomicLong, client: ActorRef) extends Actor with Stash with ActorLogging { client ! ElectrumClient.AddStatusListener(self) @@ -162,7 +163,7 @@ class ElectrumWatcher(client: ActorRef) extends Actor with Stash with ActorLoggi case ElectrumClient.ServerError(ElectrumClient.GetTransaction(txid, Some(origin: ActorRef)), _) => origin ! GetTxWithMetaResponse(txid, None, tip.time) case PublishAsap(tx) => - val blockCount = Globals.blockCount.get() + val blockCount = this.blockCount.get() val cltvTimeout = Scripts.cltvTimeout(tx) val csvTimeout = Scripts.csvTimeout(tx) if (csvTimeout > 0) { @@ -183,7 +184,7 @@ class ElectrumWatcher(client: ActorRef) extends Actor with Stash with ActorLoggi case WatchEventConfirmed(BITCOIN_PARENT_TX_CONFIRMED(tx), blockHeight, _, _) => log.info(s"parent tx of txid=${tx.txid} has been confirmed") - val blockCount = Globals.blockCount.get() + val blockCount = this.blockCount.get() val csvTimeout = Scripts.csvTimeout(tx) val absTimeout = blockHeight + csvTimeout if (absTimeout > blockCount) { @@ -211,38 +212,3 @@ class ElectrumWatcher(client: ActorRef) extends Actor with Stash with ActorLoggi } } - -object ElectrumWatcher extends App { - - val system = ActorSystem() - - import scala.concurrent.ExecutionContext.Implicits.global - - class Root extends Actor with ActorLogging { - val client = context.actorOf(Props(new ElectrumClient(new InetSocketAddress("localhost", 51000), ssl = SSL.OFF)), "client") - client ! ElectrumClient.AddStatusListener(self) - - override def unhandled(message: Any): Unit = { - super.unhandled(message) - log.warning(s"unhandled message $message") - } - - def receive = { - case ElectrumClient.ElectrumReady(_, _, _) => - log.info(s"starting watcher") - context become running(context.actorOf(Props(new ElectrumWatcher(client)), "watcher")) - } - - def running(watcher: ActorRef): Receive = { - case watch: Watch => watcher forward watch - } - } - - val root = system.actorOf(Props[Root], "root") - val scanner = new java.util.Scanner(System.in) - while (true) { - val tx = Transaction.read(scanner.nextLine()) - root ! WatchSpent(root, tx.txid, 0, tx.txOut(0).publicKeyScript, BITCOIN_FUNDING_SPENT) - root ! WatchConfirmed(root, tx.txid, tx.txOut(0).publicKeyScript, 4L, BITCOIN_FUNDING_DEPTHOK) - } -} diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala index 3c6577f792..616c3b2b39 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala @@ -601,7 +601,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId handleCommandError(AddHtlcFailed(d.channelId, c.paymentHash, error, origin(c), Some(d.channelUpdate), Some(c)), c) case Event(c: CMD_ADD_HTLC, d: DATA_NORMAL) => - Try(Commitments.sendAdd(d.commitments, c, origin(c))) match { + Try(Commitments.sendAdd(d.commitments, c, origin(c), nodeParams.currentBlockHeight)) match { case Success(Right((commitments1, add))) => if (c.commit) self ! CMD_SIGN handleCommandSuccess(sender, d.copy(commitments = commitments1)) sending add 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 e213d8161f..98e8062eea 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 @@ -25,7 +25,7 @@ import fr.acinq.eclair.payment._ import fr.acinq.eclair.transactions.Transactions._ import fr.acinq.eclair.transactions._ import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{Globals, MilliSatoshi, _} +import fr.acinq.eclair.{MilliSatoshi, _} // @formatter:off case class LocalChanges(proposed: List[UpdateMessage], signed: List[UpdateMessage], acked: List[UpdateMessage]) { @@ -120,17 +120,16 @@ object Commitments { * @param cmd add HTLC command * @return either Left(failure, error message) where failure is a failure message (see BOLT #4 and the Failure Message class) or Right((new commitments, updateAddHtlc) */ - def sendAdd(commitments: Commitments, cmd: CMD_ADD_HTLC, origin: Origin): Either[ChannelException, (Commitments, UpdateAddHtlc)] = { - val blockCount = Globals.blockCount.get() + def sendAdd(commitments: Commitments, cmd: CMD_ADD_HTLC, origin: Origin, blockHeight: Long): Either[ChannelException, (Commitments, UpdateAddHtlc)] = { // 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 = Channel.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry + val minExpiry = Channel.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry(blockHeight) if (cmd.cltvExpiry < minExpiry) { - return Left(ExpiryTooSmall(commitments.channelId, minimum = minExpiry, actual = cmd.cltvExpiry, blockCount = blockCount)) + return Left(ExpiryTooSmall(commitments.channelId, minimum = minExpiry, actual = cmd.cltvExpiry, blockCount = blockHeight)) } - val maxExpiry = Channel.MAX_CLTV_EXPIRY_DELTA.toCltvExpiry + val maxExpiry = Channel.MAX_CLTV_EXPIRY_DELTA.toCltvExpiry(blockHeight) // 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.cltvExpiry >= maxExpiry) { - return Left(ExpiryTooBig(commitments.channelId, maximum = maxExpiry, actual = cmd.cltvExpiry, blockCount = blockCount)) + return Left(ExpiryTooBig(commitments.channelId, maximum = maxExpiry, actual = cmd.cltvExpiry, blockCount = blockHeight)) } if (cmd.amount < commitments.remoteParams.htlcMinimum) { 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 1e43a04392..9d3cea6cbc 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 @@ -22,7 +22,7 @@ import fr.acinq.eclair.channel.{CMD_FAIL_HTLC, CMD_FULFILL_HTLC, Channel} import fr.acinq.eclair.db.IncomingPayment import fr.acinq.eclair.payment.PaymentLifecycle.ReceivePayment import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{Globals, NodeParams, randomBytes32} +import fr.acinq.eclair.{NodeParams, randomBytes32} import scala.compat.Platform import scala.concurrent.ExecutionContext @@ -59,30 +59,31 @@ class LocalPaymentHandler(nodeParams: NodeParams) extends Actor with ActorLoggin case htlc: UpdateAddHtlc => paymentDb.getPendingPaymentRequestAndPreimage(htlc.paymentHash) match { case Some((paymentPreimage, paymentRequest)) => - val minFinalExpiry = paymentRequest.minFinalCltvExpiryDelta.getOrElse(Channel.MIN_CLTV_EXPIRY_DELTA).toCltvExpiry + val minFinalExpiry = paymentRequest.minFinalCltvExpiryDelta.getOrElse(Channel.MIN_CLTV_EXPIRY_DELTA).toCltvExpiry(nodeParams.currentBlockHeight) // 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 paymentRequest.isExpired => - sender ! CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(htlc.amountMsat, Globals.blockCount.get())), commit = true) + sender ! CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(htlc.amountMsat, nodeParams.currentBlockHeight)), commit = true) case _ if htlc.cltvExpiry < minFinalExpiry => - sender ! CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(htlc.amountMsat, Globals.blockCount.get())), commit = true) + sender ! CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(htlc.amountMsat, nodeParams.currentBlockHeight)), commit = true) case Some(amount) if htlc.amountMsat < amount => log.warning(s"received payment with amount too small for paymentHash=${htlc.paymentHash} amount=${htlc.amountMsat}") - sender ! CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(htlc.amountMsat, Globals.blockCount.get())), commit = true) + sender ! CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(htlc.amountMsat, nodeParams.currentBlockHeight)), commit = true) case Some(amount) if htlc.amountMsat > amount * 2 => log.warning(s"received payment with amount too large for paymentHash=${htlc.paymentHash} amount=${htlc.amountMsat}") - sender ! CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(htlc.amountMsat, Globals.blockCount.get())), commit = true) + sender ! CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(htlc.amountMsat, nodeParams.currentBlockHeight)), commit = true) case _ => log.info(s"received payment for paymentHash=${htlc.paymentHash} amount=${htlc.amountMsat}") // amount is correct or was not specified in the payment request nodeParams.db.payments.addIncomingPayment(IncomingPayment(htlc.paymentHash, htlc.amountMsat, Platform.currentTime)) sender ! CMD_FULFILL_HTLC(htlc.id, paymentPreimage, commit = true) context.system.eventStream.publish(PaymentReceived(htlc.amountMsat, htlc.paymentHash, htlc.channelId)) + } case None => - sender ! CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(htlc.amountMsat, Globals.blockCount.get())), commit = true) + sender ! CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(htlc.amountMsat, nodeParams.currentBlockHeight)), commit = true) } } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentInitiator.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentInitiator.scala index a3db9e6dc8..f1b6acf1df 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentInitiator.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentInitiator.scala @@ -37,7 +37,7 @@ class PaymentInitiator(nodeParams: NodeParams, router: ActorRef, register: Actor case p: PaymentInitiator.SendPaymentRequest => val paymentId = UUID.randomUUID() // We add one block in order to not have our htlc fail when a new block has just been found. - val finalExpiry = (p.finalExpiryDelta + 1).toCltvExpiry + val finalExpiry = p.finalExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight + 1) val payFsm = context.actorOf(PaymentLifecycle.props(nodeParams, paymentId, router, register)) // NB: we only generate legacy payment onions for now for maximum compatibility. p.predefinedRoute match { 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 ca93d70a7e..c907524ccd 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 @@ -412,7 +412,7 @@ class Router(val nodeParams: NodeParams, watcher: ActorRef, initialized: Option[ case Event(TickPruneStaleChannels, d) => // first we select channels that we will prune - val staleChannels = getStaleChannels(d.channels.values) + val staleChannels = getStaleChannels(d.channels.values, nodeParams.currentBlockHeight) val staleChannelIds = staleChannels.map(_.ann.shortChannelId) // then we remove nodes that aren't tied to any channels anymore (and deduplicate them) val potentialStaleNodes = staleChannels.flatMap(c => Set(c.ann.nodeId1, c.ann.nodeId2)).toSet @@ -493,7 +493,7 @@ class Router(val nodeParams: NodeParams, watcher: ActorRef, initialized: Option[ log.info(s"finding a route $start->$end with assistedChannels={} ignoreNodes={} ignoreChannels={} excludedChannels={}", assistedChannels.keys.mkString(","), ignoreNodes.map(_.value).mkString(","), ignoreChannels.mkString(","), d.excludedChannels.mkString(",")) log.info(s"finding a route with randomize={} params={}", routesToFind > 1, params) - findRoute(d.graph, start, end, amount, numRoutes = routesToFind, extraEdges = extraEdges, ignoredEdges = ignoredEdges, ignoredVertices = ignoreNodes, routeParams = params) + findRoute(d.graph, start, end, amount, numRoutes = routesToFind, extraEdges = extraEdges, ignoredEdges = ignoredEdges, ignoredVertices = ignoreNodes, routeParams = params, nodeParams.currentBlockHeight) .map(r => sender ! RouteResponse(r, ignoreNodes, ignoreChannels)) .recover { case t => sender ! Status.Failure(t) } stay @@ -938,15 +938,15 @@ object Router { * @param update2_opt update corresponding to the other side of the channel, if we have it * @return */ - def isStale(channel: ChannelAnnouncement, update1_opt: Option[ChannelUpdate], update2_opt: Option[ChannelUpdate]): Boolean = { + def isStale(channel: ChannelAnnouncement, update1_opt: Option[ChannelUpdate], update2_opt: Option[ChannelUpdate], currentBlockHeight: Long): Boolean = { // BOLT 7: "nodes MAY prune channels should the timestamp of the latest channel_update be older than 2 weeks (1209600 seconds)" // but we don't want to prune brand new channels for which we didn't yet receive a channel update, so we keep them as long as they are less than 2 weeks (2016 blocks) old - val staleThresholdBlocks = Globals.blockCount.get() - 2016 + val staleThresholdBlocks = currentBlockHeight - 2016 val TxCoordinates(blockHeight, _, _) = ShortChannelId.coordinates(channel.shortChannelId) blockHeight < staleThresholdBlocks && update1_opt.forall(isStale) && update2_opt.forall(isStale) } - def getStaleChannels(channels: Iterable[PublicChannel]): Iterable[PublicChannel] = channels.filter(data => isStale(data.ann, data.update_1_opt, data.update_2_opt)) + def getStaleChannels(channels: Iterable[PublicChannel], currentBlockHeight: Long): Iterable[PublicChannel] = channels.filter(data => isStale(data.ann, data.update_1_opt, data.update_2_opt, currentBlockHeight)) /** * Filters channels that we want to send to nodes asking for a channel range @@ -1226,12 +1226,11 @@ object Router { extraEdges: Set[GraphEdge] = Set.empty, ignoredEdges: Set[ChannelDesc] = Set.empty, ignoredVertices: Set[PublicKey] = Set.empty, - routeParams: RouteParams): Try[Seq[Hop]] = Try { + routeParams: RouteParams, + currentBlockHeight: Long): Try[Seq[Hop]] = Try { if (localNodeId == targetNodeId) throw CannotRouteToSelf - val currentBlockHeight = Globals.blockCount.get() - def feeBaseOk(fee: MilliSatoshi): Boolean = fee <= routeParams.maxFeeBase def feePctOk(fee: MilliSatoshi, amount: MilliSatoshi): Boolean = { @@ -1251,7 +1250,7 @@ object Router { val foundRoutes = Graph.yenKshortestPaths(g, localNodeId, targetNodeId, amount, ignoredEdges, ignoredVertices, extraEdges, numRoutes, routeParams.ratios, currentBlockHeight, boundaries).toList match { case Nil if routeParams.routeMaxLength < ROUTE_MAX_LENGTH => // if not found within the constraints we relax and repeat the search - return findRoute(g, localNodeId, targetNodeId, amount, numRoutes, extraEdges, ignoredEdges, ignoredVertices, routeParams.copy(routeMaxLength = ROUTE_MAX_LENGTH, routeMaxCltv = DEFAULT_ROUTE_MAX_CLTV)) + return findRoute(g, localNodeId, targetNodeId, amount, numRoutes, extraEdges, ignoredEdges, ignoredVertices, routeParams.copy(routeMaxLength = ROUTE_MAX_LENGTH, routeMaxCltv = DEFAULT_ROUTE_MAX_CLTV), currentBlockHeight) case Nil => throw RouteNotFound case routes => routes.find(_.path.size == 1) match { case Some(directRoute) => directRoute :: Nil diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/CltvExpirySpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/CltvExpirySpec.scala index 779c58a999..76c1ca0922 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/CltvExpirySpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/CltvExpirySpec.scala @@ -39,10 +39,8 @@ class CltvExpirySpec extends FunSuite { assert(d > CltvExpiryDelta(560)) // convert to cltv expiry - Globals.blockCount.set(1105) - assert(d.toCltvExpiry === CltvExpiry(1666)) - Globals.blockCount.set(1106) - assert(d.toCltvExpiry === CltvExpiry(1667)) + assert(d.toCltvExpiry(blockHeight = 1105) === CltvExpiry(1666)) + assert(d.toCltvExpiry(blockHeight = 1106) === CltvExpiry(1667)) } test("cltv expiry") { diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/StartupSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/StartupSpec.scala index da1edf35f7..e0931dba52 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/StartupSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/StartupSpec.scala @@ -16,10 +16,13 @@ package fr.acinq.eclair +import java.util.concurrent.atomic.AtomicLong + import com.typesafe.config.ConfigFactory import fr.acinq.bitcoin.Block import fr.acinq.eclair.crypto.LocalKeyManager import org.scalatest.FunSuite + import scala.util.Try class StartupSpec extends FunSuite { @@ -42,8 +45,10 @@ class StartupSpec extends FunSuite { val conf = illegalAliasConf.withFallback(ConfigFactory.parseResources("reference.conf").getConfig("eclair")) val keyManager = new LocalKeyManager(seed = randomBytes32, chainHash = Block.TestnetGenesisBlock.hash) + val blockCount = new AtomicLong(0) + // try to create a NodeParams instance with a conf that contains an illegal alias - val nodeParamsAttempt = Try(NodeParams.makeNodeParams(conf, keyManager, None, TestConstants.inMemoryDb(), new TestConstants.TestFeeEstimator)) + val nodeParamsAttempt = Try(NodeParams.makeNodeParams(conf, keyManager, None, TestConstants.inMemoryDb(), blockCount, new TestConstants.TestFeeEstimator)) assert(nodeParamsAttempt.isFailure && nodeParamsAttempt.failed.get.getMessage.contains("alias, too long")) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala b/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala index ae1d63138a..c0b57b4956 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala @@ -17,6 +17,7 @@ package fr.acinq.eclair import java.sql.{Connection, DriverManager} +import java.util.concurrent.atomic.AtomicLong import fr.acinq.bitcoin.Crypto.PrivateKey import fr.acinq.bitcoin.{Block, ByteVector32, Script} @@ -65,6 +66,7 @@ object TestConstants { // This is a function, and not a val! When called will return a new NodeParams def nodeParams = NodeParams( keyManager = keyManager, + blockCount = new AtomicLong(400000), alias = "alice", color = Color(1, 2, 3), publicAddresses = NodeAddress.fromParts("localhost", 9731).get :: Nil, @@ -141,6 +143,7 @@ object TestConstants { def nodeParams = NodeParams( keyManager = keyManager, + blockCount = new AtomicLong(400000), alias = "bob", color = Color(4, 5, 6), publicAddresses = NodeAddress.fromParts("localhost", 9732).get :: Nil, diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/TestUtils.scala b/eclair-core/src/test/scala/fr/acinq/eclair/TestUtils.scala index 4f5ecdcb6f..7213a9ff96 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/TestUtils.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/TestUtils.scala @@ -17,6 +17,7 @@ package fr.acinq.eclair import java.io.File +import java.net.ServerSocket object TestUtils { @@ -27,4 +28,18 @@ object TestUtils { .props .get("buildDirectory") // this is defined if we run from maven .getOrElse(new File(sys.props("user.dir"), "target").getAbsolutePath) // otherwise we probably are in intellij, so we build it manually assuming that user.dir == path to the module + + + def availablePort: Int = synchronized { + var serverSocket: ServerSocket = null + try { + serverSocket = new ServerSocket(0) + serverSocket.getLocalPort + } finally { + if (serverSocket != null) { + serverSocket.close() + } + } + } + } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/TestkitBaseClass.scala b/eclair-core/src/test/scala/fr/acinq/eclair/TestkitBaseClass.scala index 0aaa6fd811..ca88758426 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/TestkitBaseClass.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/TestkitBaseClass.scala @@ -30,10 +30,6 @@ import scala.concurrent.Await */ abstract class TestkitBaseClass extends TestKit(ActorSystem("test")) with fixture.FunSuiteLike with BeforeAndAfterEach with BeforeAndAfterAll { - override def beforeAll { - Globals.blockCount.set(400000) - } - override def afterEach() { system.actorSelection(system / "*") ! PoisonPill intercept[ActorNotFound] { 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 c16c1d6e7d..136e09822e 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 @@ -45,8 +45,8 @@ class BitcoinCoreWalletSpec extends TestKit(ActorSystem("test")) with BitcoindSe "eclair.chain" -> "regtest", "eclair.spv" -> false, "eclair.server.public-ips.1" -> "localhost", - "eclair.bitcoind.port" -> 28333, - "eclair.bitcoind.rpcport" -> 28332, + "eclair.bitcoind.port" -> bitcoindPort, + "eclair.bitcoind.rpcport" -> bitcoindRpcPort, "eclair.router-broadcast-interval" -> "2 second", "eclair.auto-reconnect" -> false)) val config = ConfigFactory.load(commonConfig).getConfig("eclair") 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 518e6ed1a7..359d6f88e0 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 @@ -28,10 +28,11 @@ import fr.acinq.eclair.TestUtils import fr.acinq.eclair.blockchain.bitcoind.rpc.{BasicBitcoinJsonRPCClient, BitcoinJsonRPCClient} import fr.acinq.eclair.integration.IntegrationSpec import grizzled.slf4j.Logging -import org.json4s.JsonAST.JValue +import org.json4s.JsonAST.{JArray, JDecimal, JInt, JString, JValue} import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ +import scala.io.Source trait BitcoindService extends Logging { self: TestKitBase => @@ -39,6 +40,14 @@ trait BitcoindService extends Logging { implicit val system: ActorSystem implicit val sttpBackend = OkHttpFutureBackend() + val bitcoindPort: Int = TestUtils.availablePort + + val bitcoindRpcPort: Int = TestUtils.availablePort + + val bitcoindZmqBlockPort: Int = TestUtils.availablePort + + val bitcoindZmqTxPort: Int = TestUtils.availablePort + import scala.sys.process._ val INTEGRATION_TMP_DIR = new File(TestUtils.BUILD_DIRECTORY, s"integration-${UUID.randomUUID()}") @@ -56,11 +65,17 @@ trait BitcoindService extends Logging { def startBitcoind(): Unit = { Files.createDirectories(PATH_BITCOIND_DATADIR.toPath) if (!Files.exists(new File(PATH_BITCOIND_DATADIR.toString, "bitcoin.conf").toPath)) { - Files.copy(classOf[IntegrationSpec].getResourceAsStream("/integration/bitcoin.conf"), new File(PATH_BITCOIND_DATADIR.toString, "bitcoin.conf").toPath, StandardCopyOption.REPLACE_EXISTING) + val is = classOf[IntegrationSpec].getResourceAsStream("/integration/bitcoin.conf") + val conf = Source.fromInputStream(is).mkString + .replace("28333", bitcoindPort.toString) + .replace("28332", bitcoindRpcPort.toString) + .replace("28334", bitcoindZmqBlockPort.toString) + .replace("28335", bitcoindZmqTxPort.toString) + Files.writeString(new File(PATH_BITCOIND_DATADIR.toString, "bitcoin.conf").toPath, conf) } bitcoind = s"$PATH_BITCOIND -datadir=$PATH_BITCOIND_DATADIR".run() - bitcoinrpcclient = new BasicBitcoinJsonRPCClient(user = "foo", password = "bar", host = "localhost", port = 28332) + bitcoinrpcclient = new BasicBitcoinJsonRPCClient(user = "foo", password = "bar", host = "localhost", port = bitcoindRpcPort) bitcoincli = system.actorOf(Props(new Actor { override def receive: Receive = { case BitcoinReq(method) => bitcoinrpcclient.invoke(method) pipeTo sender @@ -83,11 +98,23 @@ trait BitcoindService extends Logging { logger.info(s"waiting for bitcoind to initialize...") awaitCond({ sender.send(bitcoincli, BitcoinReq("getnetworkinfo")) - sender.receiveOne(5 second).isInstanceOf[JValue] - }, max = 30 seconds, interval = 500 millis) + sender.expectMsgType[Any](5 second) match { + case j: JValue => j \ "version" match { + case JInt(_) => true + case _ => false + } + case _ => false + } + }, max = 3 minutes, interval = 2 seconds) logger.info(s"generating initial blocks...") sender.send(bitcoincli, BitcoinReq("generate", 150)) - sender.expectMsgType[JValue](30 seconds) + val JArray(res) = sender.expectMsgType[JValue](3 minutes) + assert(res.size == 150) + awaitCond({ + sender.send(bitcoincli, BitcoinReq("getbalance")) + val JDecimal(balance) = sender.expectMsgType[JDecimal](30 seconds) + balance > 100 + }, max = 3 minutes, interval = 2 second) } } \ No newline at end of file diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/ExtendedBitcoinClientSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/ExtendedBitcoinClientSpec.scala index 945c6db054..86e139e54c 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/ExtendedBitcoinClientSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/ExtendedBitcoinClientSpec.scala @@ -38,8 +38,8 @@ class ExtendedBitcoinClientSpec extends TestKit(ActorSystem("test")) with Bitcoi "eclair.chain" -> "regtest", "eclair.spv" -> false, "eclair.server.public-ips.1" -> "localhost", - "eclair.bitcoind.port" -> 28333, - "eclair.bitcoind.rpcport" -> 28332, + "eclair.bitcoind.port" -> bitcoindPort, + "eclair.bitcoind.rpcport" -> bitcoindRpcPort, "eclair.router-broadcast-interval" -> "2 second", "eclair.auto-reconnect" -> false)) val config = ConfigFactory.load(commonConfig).getConfig("eclair") diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/electrum/ElectrumClientPoolSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/electrum/ElectrumClientPoolSpec.scala index 7227c5f59d..0aca5ca3ee 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/electrum/ElectrumClientPoolSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/electrum/ElectrumClientPoolSpec.scala @@ -17,6 +17,7 @@ package fr.acinq.eclair.blockchain.electrum import java.net.InetSocketAddress +import java.util.concurrent.atomic.AtomicLong import akka.actor.{ActorRef, ActorSystem, Props} import akka.testkit.{TestKit, TestProbe} @@ -66,7 +67,7 @@ class ElectrumClientPoolSpec extends TestKit(ActorSystem("test")) with FunSuiteL val addresses = random.shuffle(serverAddresses.toSeq).take(2).toSet + ElectrumClientPool.ElectrumServerAddress(new InetSocketAddress("electrum.acinq.co", 50002), SSL.STRICT) stream.close() assert(addresses.nonEmpty) - pool = system.actorOf(Props(new ElectrumClientPool(addresses)), "electrum-client") + pool = system.actorOf(Props(new ElectrumClientPool(new AtomicLong(), addresses)), "electrum-client") } test("connect to an electrumx mainnet server") { 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 b7e26e7b98..ee18f3e7ce 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 @@ -18,6 +18,7 @@ package fr.acinq.eclair.blockchain.electrum import java.net.InetSocketAddress import java.sql.DriverManager +import java.util.concurrent.atomic.AtomicLong import akka.actor.{ActorRef, ActorSystem, Props} import akka.testkit.{TestKit, TestProbe} @@ -87,7 +88,7 @@ class ElectrumWalletSpec extends TestKit(ActorSystem("test")) with FunSuiteLike } test("wait until wallet is ready") { - electrumClient = system.actorOf(Props(new ElectrumClientPool(Set(ElectrumServerAddress(new InetSocketAddress("localhost", electrumPort), SSL.OFF))))) + electrumClient = system.actorOf(Props(new ElectrumClientPool(new AtomicLong(), Set(ElectrumServerAddress(new InetSocketAddress("localhost", electrumPort), SSL.OFF))))) wallet = system.actorOf(Props(new ElectrumWallet(seed, electrumClient, WalletParameters(Block.RegtestGenesisBlock.hash, new SqliteWalletDb(DriverManager.getConnection("jdbc:sqlite::memory:")), minimumFee = 5000 sat))), "wallet") val probe = TestProbe() awaitCond({ diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/electrum/ElectrumWatcherSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/electrum/ElectrumWatcherSpec.scala index 09c7ce7af5..38d0f92476 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/electrum/ElectrumWatcherSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/electrum/ElectrumWatcherSpec.scala @@ -17,6 +17,7 @@ package fr.acinq.eclair.blockchain.electrum import java.net.InetSocketAddress +import java.util.concurrent.atomic.AtomicLong import akka.actor.{ActorSystem, Props} import akka.testkit.{TestKit, TestProbe} @@ -55,8 +56,9 @@ class ElectrumWatcherSpec extends TestKit(ActorSystem("test")) with FunSuiteLike test("watch for confirmed transactions") { val probe = TestProbe() - val electrumClient = system.actorOf(Props(new ElectrumClientPool(Set(electrumAddress)))) - val watcher = system.actorOf(Props(new ElectrumWatcher(electrumClient))) + val blockCount = new AtomicLong() + val electrumClient = system.actorOf(Props(new ElectrumClientPool(blockCount, Set(electrumAddress)))) + val watcher = system.actorOf(Props(new ElectrumWatcher(blockCount, electrumClient))) probe.send(bitcoincli, BitcoinReq("getnewaddress")) val JString(address) = probe.expectMsgType[JValue] @@ -80,8 +82,9 @@ class ElectrumWatcherSpec extends TestKit(ActorSystem("test")) with FunSuiteLike test("watch for spent transactions") { val probe = TestProbe() - val electrumClient = system.actorOf(Props(new ElectrumClientPool(Set(electrumAddress)))) - val watcher = system.actorOf(Props(new ElectrumWatcher(electrumClient))) + val blockCount = new AtomicLong() + val electrumClient = system.actorOf(Props(new ElectrumClientPool(blockCount, Set(electrumAddress)))) + val watcher = system.actorOf(Props(new ElectrumWatcher(blockCount, electrumClient))) probe.send(bitcoincli, BitcoinReq("getnewaddress")) val JString(address) = probe.expectMsgType[JValue] @@ -124,9 +127,10 @@ class ElectrumWatcherSpec extends TestKit(ActorSystem("test")) with FunSuiteLike } test("get transaction") { + val blockCount = new AtomicLong() val mainnetAddress = ElectrumServerAddress(new InetSocketAddress("electrum.acinq.co", 50002), SSL.STRICT) - val electrumClient = system.actorOf(Props(new ElectrumClientPool(Set(mainnetAddress)))) - val watcher = system.actorOf(Props(new ElectrumWatcher(electrumClient))) + val electrumClient = system.actorOf(Props(new ElectrumClientPool(blockCount, Set(mainnetAddress)))) + val watcher = system.actorOf(Props(new ElectrumWatcher(blockCount, electrumClient))) //Thread.sleep(10000) val probe = TestProbe() diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/electrum/ElectrumxService.scala b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/electrum/ElectrumxService.scala index ba1266f04a..71b487f3e8 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/electrum/ElectrumxService.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/electrum/ElectrumxService.scala @@ -20,26 +20,28 @@ import com.spotify.docker.client.{DefaultDockerClient, DockerClient} import com.whisk.docker.impl.spotify.SpotifyDockerFactory import com.whisk.docker.scalatest.DockerTestKit import com.whisk.docker.{DockerContainer, DockerFactory} +import fr.acinq.eclair.TestUtils +import fr.acinq.eclair.blockchain.bitcoind.BitcoindService import org.scalatest.Suite trait ElectrumxService extends DockerTestKit { - self: Suite => + self: Suite with BitcoindService => - val electrumPort = 47000 + val electrumPort = TestUtils.availablePort val electrumxContainer = if (System.getProperty("os.name").startsWith("Linux")) { // "host" mode will let the container access the host network on linux // we use our own docker image because other images on Docker lag behind and don't yet support 1.4 DockerContainer("acinq/electrumx") .withNetworkMode("host") - .withEnv("DAEMON_URL=http://foo:bar@localhost:28332", "COIN=BitcoinSegwit", "NET=regtest", s"TCP_PORT=$electrumPort") + .withEnv(s"DAEMON_URL=http://foo:bar@localhost:$bitcoindRpcPort", "COIN=BitcoinSegwit", "NET=regtest", s"TCP_PORT=$electrumPort") //.withLogLineReceiver(LogLineReceiver(true, println)) } else { // on windows or oxs, host mode is not available, but from docker 18.03 on host.docker.internal can be used instead // host.docker.internal is not (yet ?) available on linux though DockerContainer("acinq/electrumx") .withPorts(electrumPort -> Some(electrumPort)) - .withEnv("DAEMON_URL=http://foo:bar@host.docker.internal:28332", "COIN=BitcoinSegwit", "NET=regtest", s"TCP_PORT=$electrumPort") + .withEnv(s"DAEMON_URL=http://foo:bar@host.docker.internal:$bitcoindRpcPort", "COIN=BitcoinSegwit", "NET=regtest", s"TCP_PORT=$electrumPort") //.withLogLineReceiver(LogLineReceiver(true, println)) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/fee/BitcoinCoreFeeProviderSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/fee/BitcoinCoreFeeProviderSpec.scala index 126218bab9..1ad9f555ff 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/fee/BitcoinCoreFeeProviderSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/fee/BitcoinCoreFeeProviderSpec.scala @@ -42,8 +42,8 @@ class BitcoinCoreFeeProviderSpec extends TestKit(ActorSystem("test")) with Bitco "eclair.chain" -> "regtest", "eclair.spv" -> false, "eclair.server.public-ips.1" -> "localhost", - "eclair.bitcoind.port" -> 28333, - "eclair.bitcoind.rpcport" -> 28332, + "eclair.bitcoind.port" -> bitcoindPort, + "eclair.bitcoind.rpcport" -> bitcoindRpcPort, "eclair.router-broadcast-interval" -> "2 second", "eclair.auto-reconnect" -> false)) val config = ConfigFactory.load(commonConfig).getConfig("eclair") diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/CommitmentsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/CommitmentsSpec.scala index e96e86e21f..eb23739a34 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/CommitmentsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/CommitmentsSpec.scala @@ -61,8 +61,8 @@ class CommitmentsSpec extends TestkitBaseClass with StateTestsHelperMethods { assert(bc0.availableBalanceForSend == b) assert(bc0.availableBalanceForReceive == a) - val (payment_preimage, cmdAdd) = makeCmdAdd(p, bob.underlyingActor.nodeParams.nodeId) - val Right((ac1, add)) = sendAdd(ac0, cmdAdd, Local(UUID.randomUUID, None)) + val (payment_preimage, cmdAdd) = makeCmdAdd(p, bob.underlyingActor.nodeParams.nodeId, currentBlockHeight) + val Right((ac1, add)) = sendAdd(ac0, cmdAdd, Local(UUID.randomUUID, None), currentBlockHeight) assert(ac1.availableBalanceForSend == a - p - fee) // as soon as htlc is sent, alice sees its balance decrease (more than the payment amount because of the commitment fees) assert(ac1.availableBalanceForReceive == b) @@ -145,8 +145,8 @@ class CommitmentsSpec extends TestkitBaseClass with StateTestsHelperMethods { assert(bc0.availableBalanceForSend == b) assert(bc0.availableBalanceForReceive == a) - val (_, cmdAdd) = makeCmdAdd(p, bob.underlyingActor.nodeParams.nodeId) - val Right((ac1, add)) = sendAdd(ac0, cmdAdd, Local(UUID.randomUUID, None)) + val (_, cmdAdd) = makeCmdAdd(p, bob.underlyingActor.nodeParams.nodeId, currentBlockHeight) + val Right((ac1, add)) = sendAdd(ac0, cmdAdd, Local(UUID.randomUUID, None), currentBlockHeight) assert(ac1.availableBalanceForSend == a - p - fee) // as soon as htlc is sent, alice sees its balance decrease (more than the payment amount because of the commitment fees) assert(ac1.availableBalanceForReceive == b) @@ -232,18 +232,18 @@ class CommitmentsSpec extends TestkitBaseClass with StateTestsHelperMethods { assert(bc0.availableBalanceForSend == b) assert(bc0.availableBalanceForReceive == a) - val (payment_preimage1, cmdAdd1) = makeCmdAdd(p1, bob.underlyingActor.nodeParams.nodeId) - val Right((ac1, add1)) = sendAdd(ac0, cmdAdd1, Local(UUID.randomUUID, None)) + val (payment_preimage1, cmdAdd1) = makeCmdAdd(p1, bob.underlyingActor.nodeParams.nodeId, currentBlockHeight) + val Right((ac1, add1)) = sendAdd(ac0, cmdAdd1, Local(UUID.randomUUID, None), currentBlockHeight) assert(ac1.availableBalanceForSend == a - p1 - fee) // as soon as htlc is sent, alice sees its balance decrease (more than the payment amount because of the commitment fees) assert(ac1.availableBalanceForReceive == b) - val (_, cmdAdd2) = makeCmdAdd(p2, bob.underlyingActor.nodeParams.nodeId) - val Right((ac2, add2)) = sendAdd(ac1, cmdAdd2, Local(UUID.randomUUID, None)) + val (_, cmdAdd2) = makeCmdAdd(p2, bob.underlyingActor.nodeParams.nodeId, currentBlockHeight) + val Right((ac2, add2)) = sendAdd(ac1, cmdAdd2, Local(UUID.randomUUID, None), currentBlockHeight) assert(ac2.availableBalanceForSend == a - p1 - fee - p2 - fee) // as soon as htlc is sent, alice sees its balance decrease (more than the payment amount because of the commitment fees) assert(ac2.availableBalanceForReceive == b) - val (payment_preimage3, cmdAdd3) = makeCmdAdd(p3, alice.underlyingActor.nodeParams.nodeId) - val Right((bc1, add3)) = sendAdd(bc0, cmdAdd3, Local(UUID.randomUUID, None)) + val (payment_preimage3, cmdAdd3) = makeCmdAdd(p3, alice.underlyingActor.nodeParams.nodeId, currentBlockHeight) + val Right((bc1, add3)) = sendAdd(bc0, cmdAdd3, Local(UUID.randomUUID, None), currentBlockHeight) assert(bc1.availableBalanceForSend == b - p3) // bob doesn't pay the fee assert(bc1.availableBalanceForReceive == a) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/FuzzySpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/FuzzySpec.scala index c1cb4abb29..f486a370fa 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/FuzzySpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/FuzzySpec.scala @@ -40,8 +40,8 @@ import scala.concurrent.duration._ import scala.util.Random /** - * Created by PM on 05/07/2016. - */ + * Created by PM on 05/07/2016. + */ class FuzzySpec extends TestkitBaseClass with StateTestsHelperMethods with Logging { @@ -81,106 +81,99 @@ class FuzzySpec extends TestkitBaseClass with StateTestsHelperMethods with Loggi bob ! WatchEventConfirmed(BITCOIN_FUNDING_DEPTHOK, 400000, 42, fundingTx) alice2blockchain.expectMsgType[WatchLost] bob2blockchain.expectMsgType[WatchLost] - awaitCond(alice.stateName == NORMAL) - awaitCond(bob.stateName == NORMAL) + awaitCond(alice.stateName == NORMAL, 1 minute) + awaitCond(bob.stateName == NORMAL, 1 minute) } withFixture(test.toNoArgTest(FixtureParam(alice, bob, pipe, relayerA, relayerB, paymentHandlerA, paymentHandlerB))) } - class SenderActor(channel: TestFSMRef[State, Data, Channel], paymentHandler: ActorRef, latch: CountDownLatch) extends Actor with ActorLogging { + class SenderActor(sendChannel: TestFSMRef[State, Data, Channel], paymentHandler: ActorRef, latch: CountDownLatch, count: Int) extends Actor with ActorLogging { // we don't want to be below htlcMinimumMsat - val requiredAmount = 1000000 + val requiredAmount = 1000000 msat def buildCmdAdd(paymentHash: ByteVector32, dest: PublicKey) = { // allow overpaying (no more than 2 times the required amount) - val amount = MilliSatoshi(requiredAmount + Random.nextInt(requiredAmount)) - val expiry = (Channel.MIN_CLTV_EXPIRY_DELTA + 1).toCltvExpiry + val amount = requiredAmount + Random.nextInt(requiredAmount.toLong.toInt).msat + val expiry = (Channel.MIN_CLTV_EXPIRY_DELTA + 1).toCltvExpiry(blockHeight = 400000) PaymentLifecycle.buildCommand(UUID.randomUUID(), paymentHash, Hop(null, dest, null) :: Nil, FinalLegacyPayload(amount, expiry))._1 } - def initiatePayment(stopping: Boolean): Unit = - if (stopping) { - context stop self + def initiatePaymentOrStop(remaining: Int): Unit = + if (remaining > 0) { + paymentHandler ! ReceivePayment(Some(requiredAmount), "One coffee") + context become { + case req: PaymentRequest => + sendChannel ! buildCmdAdd(req.paymentHash, req.nodeId) + context become { + case u: UpdateFulfillHtlc => + log.info(s"successfully sent htlc #${u.id}") + initiatePaymentOrStop(remaining - 1) + case u: UpdateFailHtlc => + log.warning(s"htlc failed: ${u.id}") + initiatePaymentOrStop(remaining - 1) + case Status.Failure(t) => + log.error(s"htlc error: ${t.getMessage}") + initiatePaymentOrStop(remaining - 1) + } + } } else { - paymentHandler ! ReceivePayment(Some(MilliSatoshi(requiredAmount)), "One coffee") - context become waitingForPaymentRequest + context stop self + latch.countDown() } - initiatePayment(false) + initiatePaymentOrStop(count) override def receive: Receive = ??? - def waitingForPaymentRequest: Receive = { - case req: PaymentRequest => - channel ! buildCmdAdd(req.paymentHash, req.nodeId) - context become waitingForFulfill(false) - } - - def waitingForFulfill(stopping: Boolean): Receive = { - case u: UpdateFulfillHtlc => - log.info(s"successfully sent htlc #${u.id}") - latch.countDown() - initiatePayment(stopping) - case u: UpdateFailHtlc => - log.warning(s"htlc failed: ${u.id}") - initiatePayment(stopping) - case Status.Failure(t) => - log.error(s"htlc error: ${t.getMessage}") - initiatePayment(stopping) - case 'stop => - log.warning(s"stopping...") - context become waitingForFulfill(true) - } - } test("fuzzy test with only one party sending HTLCs", Tag("fuzzy")) { f => import f._ - val latch = new CountDownLatch(100) - system.actorOf(Props(new SenderActor(alice, paymentHandlerB, latch))) - system.actorOf(Props(new SenderActor(alice, paymentHandlerB, latch))) + val senders = 2 + val totalMessages = 100 + val latch = new CountDownLatch(senders) + for (_ <- 0 until senders) system.actorOf(Props(new SenderActor(alice, paymentHandlerB, latch, totalMessages / senders))) awaitCond(latch.getCount == 0, max = 2 minutes) - assert(alice.stateName == NORMAL || alice.stateName == OFFLINE) - assert(bob.stateName == NORMAL || alice.stateName == OFFLINE) + assert(List(NORMAL, OFFLINE, SYNCING).contains(alice.stateName)) + assert(List(NORMAL, OFFLINE, SYNCING).contains(bob.stateName)) } test("fuzzy test with both parties sending HTLCs", Tag("fuzzy")) { f => import f._ - val latch = new CountDownLatch(100) - system.actorOf(Props(new SenderActor(alice, paymentHandlerB, latch))) - system.actorOf(Props(new SenderActor(alice, paymentHandlerB, latch))) - system.actorOf(Props(new SenderActor(bob, paymentHandlerA, latch))) - system.actorOf(Props(new SenderActor(bob, paymentHandlerA, latch))) + val senders = 2 + val totalMessages = 100 + val latch = new CountDownLatch(2 * senders) + for (_ <- 0 until senders) system.actorOf(Props(new SenderActor(alice, paymentHandlerB, latch, totalMessages / senders))) + for (_ <- 0 until senders) system.actorOf(Props(new SenderActor(bob, paymentHandlerA, latch, totalMessages / senders))) awaitCond(latch.getCount == 0, max = 2 minutes) - assert(alice.stateName == NORMAL || alice.stateName == OFFLINE) - assert(bob.stateName == NORMAL || alice.stateName == OFFLINE) + assert(List(NORMAL, OFFLINE, SYNCING).contains(alice.stateName)) + assert(List(NORMAL, OFFLINE, SYNCING).contains(bob.stateName)) } - test("one party sends lots of htlcs send shutdown") { f => + test("one party sends lots of htlcs then shutdown") { f => import f._ - val latch = new CountDownLatch(20) - val senders = system.actorOf(Props(new SenderActor(alice, paymentHandlerB, latch))) :: - system.actorOf(Props(new SenderActor(alice, paymentHandlerB, latch))) :: - system.actorOf(Props(new SenderActor(alice, paymentHandlerB, latch))) :: Nil + val senders = 2 + val totalMessages = 20 + val latch = new CountDownLatch(senders) + for (_ <- 0 until senders) system.actorOf(Props(new SenderActor(alice, paymentHandlerB, latch, totalMessages / senders))) awaitCond(latch.getCount == 0, max = 2 minutes) val sender = TestProbe() awaitCond({ sender.send(alice, CMD_CLOSE(None)) sender.expectMsgAnyClassOf(classOf[String], classOf[Status.Failure]) == "ok" }, max = 30 seconds) - senders.foreach(_ ! 'stop) - awaitCond(alice.stateName == CLOSING) - awaitCond(alice.stateName == CLOSING) + awaitCond(alice.stateName == CLOSING, max = 3 minutes, interval = 1 second) + awaitCond(bob.stateName == CLOSING, max = 3 minutes, interval = 1 second) } - test("both parties send lots of htlcs send shutdown") { f => + test("both parties send lots of htlcs then shutdown") { f => import f._ - val latch = new CountDownLatch(30) - val senders = system.actorOf(Props(new SenderActor(alice, paymentHandlerB, latch))) :: - system.actorOf(Props(new SenderActor(alice, paymentHandlerB, latch))) :: - system.actorOf(Props(new SenderActor(bob, paymentHandlerA, latch))) :: - system.actorOf(Props(new SenderActor(bob, paymentHandlerA, latch))) :: Nil + val senders = 2 + val totalMessages = 100 + val latch = new CountDownLatch(2 * senders) + for (_ <- 0 until senders) system.actorOf(Props(new SenderActor(alice, paymentHandlerB, latch, totalMessages / senders))) + for (_ <- 0 until senders) system.actorOf(Props(new SenderActor(bob, paymentHandlerA, latch, totalMessages / senders))) awaitCond(latch.getCount == 0, max = 2 minutes) val sender = TestProbe() awaitCond({ @@ -191,9 +184,8 @@ class FuzzySpec extends TestkitBaseClass with StateTestsHelperMethods with Loggi // we only need that one of them succeeds resa == "ok" || resb == "ok" }, max = 30 seconds) - senders.foreach(_ ! 'stop) - awaitCond(alice.stateName == CLOSING) - awaitCond(alice.stateName == CLOSING) + awaitCond(alice.stateName == CLOSING, max = 3 minutes, interval = 1 second) + awaitCond(bob.stateName == CLOSING, max = 3 minutes, interval = 1 second) } } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/ThroughputSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/ThroughputSpec.scala index 67b4f02867..04b380baeb 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/ThroughputSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/ThroughputSpec.scala @@ -39,7 +39,8 @@ class ThroughputSpec extends FunSuite { ignore("throughput") { implicit val system = ActorSystem() val pipe = system.actorOf(Props[Pipe], "pipe") - val blockchain = system.actorOf(ZmqWatcher.props(new TestBitcoinClient()), "blockchain") + val blockCount = new AtomicLong() + val blockchain = system.actorOf(ZmqWatcher.props(blockCount, new TestBitcoinClient()), "blockchain") val paymentHandler = system.actorOf(Props(new Actor() { val random = new Random() diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/StateTestsHelperMethods.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/StateTestsHelperMethods.scala index a06ac26323..c1ee38216e 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/StateTestsHelperMethods.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/StateTestsHelperMethods.scala @@ -46,7 +46,9 @@ trait StateTestsHelperMethods extends TestKitBase { router: TestProbe, relayerA: TestProbe, relayerB: TestProbe, - channelUpdateListener: TestProbe) + channelUpdateListener: TestProbe) { + def currentBlockHeight = alice.underlyingActor.nodeParams.currentBlockHeight + } def init(nodeParamsA: NodeParams = TestConstants.Alice.nodeParams, nodeParamsB: NodeParams = TestConstants.Bob.nodeParams, wallet: EclairWallet = new TestWallet): SetupFixture = { val alice2bob = TestProbe() @@ -107,17 +109,18 @@ trait StateTestsHelperMethods extends TestKitBase { channelUpdateListener.expectMsgType[LocalChannelUpdate] } - def makeCmdAdd(amount: MilliSatoshi, destination: PublicKey): (ByteVector32, CMD_ADD_HTLC) = { + def makeCmdAdd(amount: MilliSatoshi, destination: PublicKey, currentBlockHeight: Long): (ByteVector32, CMD_ADD_HTLC) = { val payment_preimage: ByteVector32 = randomBytes32 val payment_hash: ByteVector32 = Crypto.sha256(payment_preimage) - val expiry = CltvExpiryDelta(144).toCltvExpiry + val expiry = CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight) val cmd = PaymentLifecycle.buildCommand(UUID.randomUUID, payment_hash, Hop(null, destination, null) :: Nil, FinalLegacyPayload(amount, expiry))._1.copy(commit = false) (payment_preimage, cmd) } def addHtlc(amount: MilliSatoshi, s: TestFSMRef[State, Data, Channel], r: TestFSMRef[State, Data, Channel], s2r: TestProbe, r2s: TestProbe): (ByteVector32, UpdateAddHtlc) = { val sender = TestProbe() - val (payment_preimage, cmd) = makeCmdAdd(amount, r.underlyingActor.nodeParams.nodeId) + val currentBlockHeight = s.underlyingActor.nodeParams.currentBlockHeight + val (payment_preimage, cmd) = makeCmdAdd(amount, r.underlyingActor.nodeParams.nodeId, currentBlockHeight) sender.send(s, cmd) sender.expectMsg("ok") val htlc = s2r.expectMsgType[UpdateAddHtlc] 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 5cccec7309..10182a87db 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 @@ -37,7 +37,7 @@ import fr.acinq.eclair.router.Announcements import fr.acinq.eclair.transactions.Transactions.{HtlcSuccessTx, htlcSuccessWeight, htlcTimeoutWeight, weight2fee} import fr.acinq.eclair.transactions.{IN, OUT, Transactions} import fr.acinq.eclair.wire.{AnnouncementSignatures, ChannelUpdate, ClosingSigned, CommitSig, Error, FailureMessageCodecs, PermanentChannelFailure, RevokeAndAck, Shutdown, UpdateAddHtlc, UpdateFailHtlc, UpdateFailMalformedHtlc, UpdateFee, UpdateFulfillHtlc} -import fr.acinq.eclair.{Globals, TestConstants, TestkitBaseClass, randomBytes32, _} +import fr.acinq.eclair.{TestConstants, TestkitBaseClass, randomBytes32, _} import org.scalatest.{Outcome, Tag} import scodec.bits._ @@ -69,7 +69,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] val sender = TestProbe() val h = randomBytes32 - val add = CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) sender.expectMsg("ok") val htlc = alice2bob.expectMsgType[UpdateAddHtlc] @@ -87,7 +87,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val sender = TestProbe() val h = randomBytes32 for (i <- 0 until 10) { - sender.send(alice, CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") val htlc = alice2bob.expectMsgType[UpdateAddHtlc] assert(htlc.id == i && htlc.paymentHash == h) @@ -99,7 +99,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] val sender = TestProbe() val h = randomBytes32 - val originHtlc = UpdateAddHtlc(channelId = randomBytes32, id = 5656, amountMsat = 50000000 msat, cltvExpiry = CltvExpiryDelta(144).toCltvExpiry, paymentHash = h, onionRoutingPacket = TestConstants.emptyOnionPacket) + val originHtlc = UpdateAddHtlc(channelId = randomBytes32, id = 5656, amountMsat = 50000000 msat, cltvExpiry = CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), paymentHash = h, onionRoutingPacket = TestConstants.emptyOnionPacket) val cmd = CMD_ADD_HTLC(originHtlc.amountMsat - 10000.msat, h, originHtlc.cltvExpiry - CltvExpiryDelta(7), TestConstants.emptyOnionPacket, upstream = Right(originHtlc)) sender.send(alice, cmd) sender.expectMsg("ok") @@ -117,11 +117,10 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - val currentBlockCount = Globals.blockCount.get - val expiryTooSmall = CltvExpiry(currentBlockCount + 3) + val expiryTooSmall = CltvExpiry(currentBlockHeight + 3) val add = CMD_ADD_HTLC(500000000 msat, randomBytes32, expiryTooSmall, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) - val error = ExpiryTooSmall(channelId(alice), Channel.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry, expiryTooSmall, currentBlockCount) + val error = ExpiryTooSmall(channelId(alice), Channel.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry(currentBlockHeight), expiryTooSmall, currentBlockHeight) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) alice2bob.expectNoMsg(200 millis) } @@ -130,11 +129,10 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - val currentBlockCount = Globals.blockCount.get - val expiryTooBig = (Channel.MAX_CLTV_EXPIRY_DELTA + 1).toCltvExpiry + val expiryTooBig = (Channel.MAX_CLTV_EXPIRY_DELTA + 1).toCltvExpiry(currentBlockHeight) val add = CMD_ADD_HTLC(500000000 msat, randomBytes32, expiryTooBig, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) - val error = ExpiryTooBig(channelId(alice), maximum = Channel.MAX_CLTV_EXPIRY_DELTA.toCltvExpiry, actual = expiryTooBig, blockCount = currentBlockCount) + val error = ExpiryTooBig(channelId(alice), maximum = Channel.MAX_CLTV_EXPIRY_DELTA.toCltvExpiry(currentBlockHeight), actual = expiryTooBig, blockCount = currentBlockHeight) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) alice2bob.expectNoMsg(200 millis) } @@ -143,7 +141,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - val add = CMD_ADD_HTLC(50 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(50 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = HtlcValueTooSmall(channelId(alice), 1000 msat, 50 msat) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) @@ -154,7 +152,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - val add = CMD_ADD_HTLC(MilliSatoshi(Int.MaxValue), randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(Int.MaxValue), randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = InsufficientFunds(channelId(alice), amount = MilliSatoshi(Int.MaxValue), missing = 1376443 sat, reserve = 20000 sat, fees = 8960 sat) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) @@ -165,7 +163,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = bob.stateData.asInstanceOf[DATA_NORMAL] - val add = CMD_ADD_HTLC(initialState.commitments.availableBalanceForSend + 1.msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(initialState.commitments.availableBalanceForSend + 1.msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(bob, add) val error = InsufficientFunds(channelId(alice), amount = add.amount, missing = 0 sat, reserve = 10000 sat, fees = 0 sat) @@ -177,16 +175,16 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - sender.send(alice, CMD_ADD_HTLC(500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] - sender.send(alice, CMD_ADD_HTLC(200000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(200000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] - sender.send(alice, CMD_ADD_HTLC(67600000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(67600000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] - val add = CMD_ADD_HTLC(1000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(1000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = InsufficientFunds(channelId(alice), amount = 1000000 msat, missing = 1000 sat, reserve = 20000 sat, fees = 12400 sat) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) @@ -197,13 +195,13 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - sender.send(alice, CMD_ADD_HTLC(300000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(300000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] - sender.send(alice, CMD_ADD_HTLC(300000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(300000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] - val add = CMD_ADD_HTLC(500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = InsufficientFunds(channelId(alice), amount = 500000000 msat, missing = 332400 sat, reserve = 20000 sat, fees = 12400 sat) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) @@ -214,7 +212,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = bob.stateData.asInstanceOf[DATA_NORMAL] - val add = CMD_ADD_HTLC(151000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(151000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(bob, add) val error = HtlcValueTooHighInFlight(channelId(bob), maximum = 150000000, actual = 151000000 msat) sender.expectMsg(Failure(AddHtlcFailed(channelId(bob), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) @@ -227,11 +225,11 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] // Bob accepts a maximum of 30 htlcs for (i <- 0 until 30) { - sender.send(alice, CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] } - val add = CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = TooManyAcceptedHtlcs(channelId(alice), maximum = 30) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) @@ -242,7 +240,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - val add1 = CMD_ADD_HTLC(TestConstants.fundingSatoshis.toMilliSatoshi * 2 / 3, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add1 = CMD_ADD_HTLC(TestConstants.fundingSatoshis.toMilliSatoshi * 2 / 3, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add1) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] @@ -250,7 +248,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] // this is over channel-capacity - val add2 = CMD_ADD_HTLC(TestConstants.fundingSatoshis.toMilliSatoshi * 2 / 3, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add2 = CMD_ADD_HTLC(TestConstants.fundingSatoshis.toMilliSatoshi * 2 / 3, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add2) val error = InsufficientFunds(channelId(alice), add2.amount, 564013 sat, 20000 sat, 10680 sat) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add2.paymentHash, error, Local(add2.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add2)))) @@ -267,7 +265,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].localShutdown.isDefined && alice.stateData.asInstanceOf[DATA_NORMAL].remoteShutdown.isEmpty) // actual test starts here - val add = CMD_ADD_HTLC(500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = NoMoreHtlcsClosingInProgress(channelId(alice)) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) @@ -279,7 +277,7 @@ 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 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add1 = CMD_ADD_HTLC(500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add1) sender.expectMsg("ok") // at the same time bob initiates a closing @@ -300,7 +298,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateAddHtlc") { f => import f._ val initialData = bob.stateData.asInstanceOf[DATA_NORMAL] - val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, 150000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, 150000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket) bob ! htlc awaitCond(bob.stateData == initialData.copy(commitments = initialData.commitments.copy(remoteChanges = initialData.commitments.remoteChanges.copy(proposed = initialData.commitments.remoteChanges.proposed :+ htlc), remoteNextHtlcId = 1))) // bob won't forward the add before it is cross-signed @@ -310,7 +308,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateAddHtlc (unexpected id)") { f => import f._ val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx - val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 42, 150000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 42, 150000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket) bob ! htlc.copy(id = 0) bob ! htlc.copy(id = 1) bob ! htlc.copy(id = 2) @@ -327,7 +325,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(ByteVector32.Zeroes, 0, 150 msat, randomBytes32, cltvExpiry = CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, 150 msat, randomBytes32, cltvExpiry = CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket) alice2bob.forward(bob, htlc) val error = bob2alice.expectMsgType[Error] assert(new String(error.data.toArray) === HtlcValueTooSmall(channelId(bob), minimum = 1000 msat, actual = 150 msat).getMessage) @@ -342,7 +340,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateAddHtlc (insufficient funds)") { f => import f._ val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx - val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(Long.MaxValue), randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(Long.MaxValue), randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket) alice2bob.forward(bob, htlc) val error = bob2alice.expectMsgType[Error] assert(new String(error.data.toArray) === InsufficientFunds(channelId(bob), amount = MilliSatoshi(Long.MaxValue), missing = 9223372036083735L sat, reserve = 20000 sat, fees = 8960 sat).getMessage) @@ -357,10 +355,10 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateAddHtlc (insufficient funds w/ pending htlcs 1/2)") { f => import f._ val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 400000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket)) - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, 200000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket)) - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, 167600000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket)) - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 3, 10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 400000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, 200000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, 167600000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 3, 10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket)) val error = bob2alice.expectMsgType[Error] assert(new String(error.data.toArray) === InsufficientFunds(channelId(bob), amount = 10000000 msat, missing = 11720 sat, reserve = 20000 sat, fees = 14120 sat).getMessage) awaitCond(bob.stateName == CLOSING) @@ -374,9 +372,9 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateAddHtlc (insufficient funds w/ pending htlcs 2/2)") { f => import f._ val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 300000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket)) - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, 300000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket)) - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, 500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 300000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, 300000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, 500000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket)) val error = bob2alice.expectMsgType[Error] assert(new String(error.data.toArray) === InsufficientFunds(channelId(bob), amount = 500000000 msat, missing = 332400 sat, reserve = 20000 sat, fees = 12400 sat).getMessage) awaitCond(bob.stateName == CLOSING) @@ -390,7 +388,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateAddHtlc (over max inflight htlc value)") { f => import f._ val tx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx - alice2bob.forward(alice, UpdateAddHtlc(ByteVector32.Zeroes, 0, 151000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket)) + alice2bob.forward(alice, UpdateAddHtlc(ByteVector32.Zeroes, 0, 151000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket)) val error = alice2bob.expectMsgType[Error] assert(new String(error.data.toArray) === HtlcValueTooHighInFlight(channelId(alice), maximum = 150000000, actual = 151000000 msat).getMessage) awaitCond(alice.stateName == CLOSING) @@ -406,9 +404,9 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx // Bob accepts a maximum of 30 htlcs for (i <- 0 until 30) { - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, i, 1000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, i, 1000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket)) } - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 30, 1000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 30, 1000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket)) val error = bob2alice.expectMsgType[Error] assert(new String(error.data.toArray) === TooManyAcceptedHtlcs(channelId(bob), maximum = 30).getMessage) awaitCond(bob.stateName == CLOSING) @@ -433,7 +431,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_SIGN (two identical htlcs in each direction)") { f => import f._ val sender = TestProbe() - val add = CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] @@ -480,19 +478,19 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { assert(a2b_2 > aliceMinOffer && a2b_2 > bobMinReceive) assert(b2a_1 > aliceMinReceive && b2a_1 > bobMinOffer) assert(b2a_2 < aliceMinReceive && b2a_2 > bobMinOffer) - sender.send(alice, CMD_ADD_HTLC(a2b_1.toMilliSatoshi, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(a2b_1.toMilliSatoshi, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] alice2bob.forward(bob) - sender.send(alice, CMD_ADD_HTLC(a2b_2.toMilliSatoshi, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(a2b_2.toMilliSatoshi, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] alice2bob.forward(bob) - sender.send(bob, CMD_ADD_HTLC(b2a_1.toMilliSatoshi, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(bob, CMD_ADD_HTLC(b2a_1.toMilliSatoshi, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") bob2alice.expectMsgType[UpdateAddHtlc] bob2alice.forward(alice) - sender.send(bob, CMD_ADD_HTLC(b2a_2.toMilliSatoshi, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(bob, CMD_ADD_HTLC(b2a_2.toMilliSatoshi, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") bob2alice.expectMsgType[UpdateAddHtlc] bob2alice.forward(alice) @@ -512,7 +510,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_SIGN (htlcs with same pubkeyScript but different amounts)") { f => import f._ val sender = TestProbe() - val add = CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) val epsilons = List(3, 1, 5, 7, 6) // unordered on purpose val htlcCount = epsilons.size for (i <- epsilons) { @@ -710,12 +708,12 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val r = randomBytes32 val h = Crypto.sha256(r) - sender.send(alice, CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") val htlc1 = alice2bob.expectMsgType[UpdateAddHtlc] alice2bob.forward(bob) - sender.send(alice, CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(50000000 msat, h, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") val htlc2 = alice2bob.expectMsgType[UpdateAddHtlc] alice2bob.forward(bob) @@ -2041,7 +2039,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // alice = 800 000 // bob = 200 000 - val add = CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(10000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/OfflineStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/OfflineStateSpec.scala index 00965f35fc..e73e50140e 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/OfflineStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/OfflineStateSpec.scala @@ -66,7 +66,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - sender.send(alice, CMD_ADD_HTLC(1000000 msat, ByteVector32.Zeroes, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(1000000 msat, ByteVector32.Zeroes, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) val ab_add_0 = alice2bob.expectMsgType[UpdateAddHtlc] // add ->b alice2bob.forward(bob) @@ -143,7 +143,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - sender.send(alice, CMD_ADD_HTLC(1000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(1000000 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) val ab_add_0 = alice2bob.expectMsgType[UpdateAddHtlc] // add ->b alice2bob.forward(bob, ab_add_0) 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 0757f56338..6dc7c367a1 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 @@ -56,7 +56,7 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // alice sends an HTLC to bob val h1 = Crypto.sha256(r1) val amount1 = 300000000 msat - val expiry1 = CltvExpiryDelta(144).toCltvExpiry + val expiry1 = CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight) val cmd1 = PaymentLifecycle.buildCommand(UUID.randomUUID, h1, Hop(null, TestConstants.Bob.nodeParams.nodeId, null) :: Nil, FinalLegacyPayload(amount1, expiry1))._1.copy(commit = false) sender.send(alice, cmd1) sender.expectMsg("ok") @@ -66,7 +66,7 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // alice sends another HTLC to bob val h2 = Crypto.sha256(r2) val amount2 = 200000000 msat - val expiry2 = CltvExpiryDelta(144).toCltvExpiry + val expiry2 = CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight) val cmd2 = PaymentLifecycle.buildCommand(UUID.randomUUID, h2, Hop(null, TestConstants.Bob.nodeParams.nodeId, null) :: Nil, FinalLegacyPayload(amount2, expiry2))._1.copy(commit = false) sender.send(alice, cmd2) sender.expectMsg("ok") 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 dd01ce607e..92dc0bae4f 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 @@ -43,7 +43,7 @@ import fr.acinq.eclair.router.{Announcements, AnnouncementsBatchValidationSpec, import fr.acinq.eclair.transactions.Transactions import fr.acinq.eclair.transactions.Transactions.{HtlcSuccessTx, HtlcTimeoutTx} import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{CltvExpiryDelta, Globals, Kit, LongToBtcAmount, MilliSatoshi, Setup, ShortChannelId, randomBytes32} +import fr.acinq.eclair.{CltvExpiryDelta, Kit, LongToBtcAmount, MilliSatoshi, Setup, ShortChannelId, randomBytes32} import grizzled.slf4j.Logging import org.json4s.JsonAST.JValue import org.json4s.{DefaultFormats, JString} @@ -51,9 +51,11 @@ import org.scalatest.{BeforeAndAfterAll, FunSuiteLike} import scodec.bits.ByteVector import scala.collection.JavaConversions._ +import scala.compat.Platform import scala.concurrent.Await import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ +import scala.util.Try /** * Created by PM on 15/03/2017. @@ -80,10 +82,10 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val commonConfig = ConfigFactory.parseMap(Map( "eclair.chain" -> "regtest", "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.bitcoind.port" -> bitcoindPort, + "eclair.bitcoind.rpcport" -> bitcoindRpcPort, + "eclair.bitcoind.zmqblock" -> s"tcp://127.0.0.1:$bitcoindZmqBlockPort", + "eclair.bitcoind.zmqtx" -> s"tcp://127.0.0.1:$bitcoindZmqTxPort", "eclair.mindepth-blocks" -> 2, "eclair.max-htlc-value-in-flight-msat" -> 100000000000L, "eclair.router.broadcast-interval" -> "2 second", @@ -345,7 +347,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService assert(failed.id == paymentId) assert(failed.paymentHash === pr.paymentHash) assert(failed.failures.size === 1) - assert(failed.failures.head.asInstanceOf[RemoteFailure].e === DecryptedFailurePacket(nodes("D").nodeParams.nodeId, IncorrectOrUnknownPaymentDetails(100000000 msat, Globals.blockCount.get()))) + assert(failed.failures.head.asInstanceOf[RemoteFailure].e === DecryptedFailurePacket(nodes("D").nodeParams.nodeId, IncorrectOrUnknownPaymentDetails(100000000 msat, getBlockCount))) } test("send an HTLC A->D with a lower amount than requested") { @@ -365,7 +367,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService assert(failed.id == paymentId) assert(failed.paymentHash === pr.paymentHash) assert(failed.failures.size === 1) - assert(failed.failures.head.asInstanceOf[RemoteFailure].e === DecryptedFailurePacket(nodes("D").nodeParams.nodeId, IncorrectOrUnknownPaymentDetails(100000000 msat, Globals.blockCount.get()))) + assert(failed.failures.head.asInstanceOf[RemoteFailure].e === DecryptedFailurePacket(nodes("D").nodeParams.nodeId, IncorrectOrUnknownPaymentDetails(100000000 msat, getBlockCount))) } test("send an HTLC A->D with too much overpayment") { @@ -385,7 +387,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService assert(paymentId == failed.id) assert(failed.paymentHash === pr.paymentHash) assert(failed.failures.size === 1) - assert(failed.failures.head.asInstanceOf[RemoteFailure].e === DecryptedFailurePacket(nodes("D").nodeParams.nodeId, IncorrectOrUnknownPaymentDetails(600000000 msat, Globals.blockCount.get()))) + assert(failed.failures.head.asInstanceOf[RemoteFailure].e === DecryptedFailurePacket(nodes("D").nodeParams.nodeId, IncorrectOrUnknownPaymentDetails(600000000 msat, getBlockCount))) } test("send an HTLC A->D with a reasonable overpayment") { @@ -458,9 +460,8 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val stateListener = TestProbe() nodes("C").system.eventStream.subscribe(stateListener.ref, classOf[ChannelStateChanged]) // first we make sure we are in sync with current blockchain height - sender.send(bitcoincli, BitcoinReq("getblockcount")) - val currentBlockCount = sender.expectMsgType[JValue](10 seconds).extract[Long] - awaitCond(Globals.blockCount.get() == currentBlockCount, max = 20 seconds, interval = 1 second) + val currentBlockCount = getBlockCount + awaitCond(getBlockCount == currentBlockCount, max = 20 seconds, interval = 1 second) // NB: F has a no-op payment handler, allowing us to manually fulfill htlcs val htlcReceiver = TestProbe() // we register this probe as the final payment handler @@ -532,15 +533,21 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService awaitAnnouncements(nodes.filterKeys(_ == "A"), 9, 11, 24) } + def getBlockCount: Long = { + // we make sure that all nodes have the same value + awaitCond(nodes.values.map(_.nodeParams.currentBlockHeight).toSet.size == 1, max = 1 minute, interval = 1 second) + // and we return it (NB: it could be a different value at this point + nodes.values.head.nodeParams.currentBlockHeight + } + test("propagate a fulfill upstream when a downstream htlc is redeemed on-chain (remote commit)") { val sender = TestProbe() // we subscribe to C's channel state transitions val stateListener = TestProbe() nodes("C").system.eventStream.subscribe(stateListener.ref, classOf[ChannelStateChanged]) // first we make sure we are in sync with current blockchain height - sender.send(bitcoincli, BitcoinReq("getblockcount")) - val currentBlockCount = sender.expectMsgType[JValue](10 seconds).extract[Long] - awaitCond(Globals.blockCount.get() == currentBlockCount, max = 20 seconds, interval = 1 second) + val currentBlockCount = getBlockCount + awaitCond(getBlockCount == currentBlockCount, max = 20 seconds, interval = 1 second) // NB: F has a no-op payment handler, allowing us to manually fulfill htlcs val htlcReceiver = TestProbe() // we register this probe as the final payment handler @@ -615,9 +622,8 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val stateListener = TestProbe() nodes("C").system.eventStream.subscribe(stateListener.ref, classOf[ChannelStateChanged]) // first we make sure we are in sync with current blockchain height - sender.send(bitcoincli, BitcoinReq("getblockcount")) - val currentBlockCount = sender.expectMsgType[JValue](10 seconds).extract[Long] - awaitCond(Globals.blockCount.get() == currentBlockCount, max = 20 seconds, interval = 1 second) + val currentBlockCount = getBlockCount + awaitCond(getBlockCount == currentBlockCount, max = 20 seconds, interval = 1 second) // NB: F has a no-op payment handler, allowing us to manually fulfill htlcs val htlcReceiver = TestProbe() // we register this probe as the final payment handler @@ -677,9 +683,8 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val stateListener = TestProbe() nodes("C").system.eventStream.subscribe(stateListener.ref, classOf[ChannelStateChanged]) // first we make sure we are in sync with current blockchain height - sender.send(bitcoincli, BitcoinReq("getblockcount")) - val currentBlockCount = sender.expectMsgType[JValue](10 seconds).extract[Long] - awaitCond(Globals.blockCount.get() == currentBlockCount, max = 20 seconds, interval = 1 second) + val currentBlockCount = getBlockCount + awaitCond(getBlockCount == currentBlockCount, max = 20 seconds, interval = 1 second) // NB: F has a no-op payment handler, allowing us to manually fulfill htlcs val htlcReceiver = TestProbe() // we register this probe as the final payment handler @@ -754,9 +759,8 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val paymentHandlerC = nodes("C").system.actorOf(LocalPaymentHandler.props(nodes("C").nodeParams)) val paymentHandlerF = nodes("F5").system.actorOf(LocalPaymentHandler.props(nodes("F5").nodeParams)) // first we make sure we are in sync with current blockchain height - sender.send(bitcoincli, BitcoinReq("getblockcount")) - val currentBlockCount = sender.expectMsgType[JValue](10 seconds).extract[Long] - awaitCond(Globals.blockCount.get() == currentBlockCount, max = 20 seconds, interval = 1 second) + val currentBlockCount = getBlockCount + awaitCond(getBlockCount == currentBlockCount, max = 20 seconds, interval = 1 second) // first we send 3 mBTC to F so that it has a balance val amountMsat = 300000000.msat sender.send(paymentHandlerF, ReceivePayment(Some(amountMsat), "1 coffee")) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala index 2772b1f591..6b3468d35b 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala @@ -17,18 +17,19 @@ package fr.acinq.eclair.interop.rustytests import java.io.File +import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.{CountDownLatch, TimeUnit} import akka.actor.{ActorRef, ActorSystem, Props} import akka.testkit.{TestFSMRef, TestKit, TestProbe} -import fr.acinq.bitcoin.{ByteVector32, Satoshi} +import fr.acinq.bitcoin.ByteVector32 import fr.acinq.eclair.TestConstants.{Alice, Bob, TestFeeEstimator} import fr.acinq.eclair.blockchain._ import fr.acinq.eclair.blockchain.fee.FeeratesPerKw import fr.acinq.eclair.channel._ import fr.acinq.eclair.payment.NoopPaymentHandler import fr.acinq.eclair.wire.Init -import fr.acinq.eclair.{Globals, LongToBtcAmount, TestUtils} +import fr.acinq.eclair.{LongToBtcAmount, TestUtils} import org.scalatest.{BeforeAndAfterAll, Matchers, Outcome, fixture} import scala.concurrent.duration._ @@ -43,7 +44,7 @@ class RustyTestsSpec extends TestKit(ActorSystem("test")) with Matchers with fix case class FixtureParam(ref: List[String], res: List[String]) override def withFixture(test: OneArgTest): Outcome = { - Globals.blockCount.set(0) + val blockCount = new AtomicLong(0) val latch = new CountDownLatch(1) val pipe: ActorRef = system.actorOf(Props(new SynchronizationPipe(latch))) val alice2blockchain = TestProbe() @@ -54,8 +55,8 @@ class RustyTestsSpec extends TestKit(ActorSystem("test")) with Matchers with fix val router = TestProbe() val wallet = new TestWallet val feeEstimator = new TestFeeEstimator - val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(Alice.nodeParams.copy(onChainFeeConf = Alice.nodeParams.onChainFeeConf.copy(feeEstimator = feeEstimator)), wallet, Bob.nodeParams.nodeId, alice2blockchain.ref, router.ref, relayer)) - val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(Bob.nodeParams.copy(onChainFeeConf = Bob.nodeParams.onChainFeeConf.copy(feeEstimator = feeEstimator)), wallet, Alice.nodeParams.nodeId, bob2blockchain.ref, router.ref, relayer)) + val alice: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(Alice.nodeParams.copy(blockCount = blockCount, onChainFeeConf = Alice.nodeParams.onChainFeeConf.copy(feeEstimator = feeEstimator)), wallet, Bob.nodeParams.nodeId, alice2blockchain.ref, router.ref, relayer)) + val bob: TestFSMRef[State, Data, Channel] = TestFSMRef(new Channel(Bob.nodeParams.copy(blockCount = blockCount, onChainFeeConf = Bob.nodeParams.onChainFeeConf.copy(feeEstimator = feeEstimator)), wallet, Alice.nodeParams.nodeId, bob2blockchain.ref, router.ref, relayer)) val aliceInit = Init(Alice.channelParams.globalFeatures, Alice.channelParams.localFeatures) val bobInit = Init(Bob.channelParams.globalFeatures, Bob.channelParams.localFeatures) // alice and bob will both have 1 000 000 sat 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 6361c3813c..592bbfd685 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 @@ -28,7 +28,7 @@ import fr.acinq.eclair.router.Hop import fr.acinq.eclair.wire.Onion.{FinalLegacyPayload, FinalTlvPayload, PerHopPayload, RelayLegacyPayload} import fr.acinq.eclair.wire.OnionTlv.{AmountToForward, OutgoingCltv} import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{CltvExpiry, CltvExpiryDelta, Globals, LongToBtcAmount, MilliSatoshi, ShortChannelId, TestConstants, nodeFee, randomBytes32} +import fr.acinq.eclair.{CltvExpiry, CltvExpiryDelta, LongToBtcAmount, MilliSatoshi, ShortChannelId, TestConstants, nodeFee, randomBytes32} import org.scalatest.{BeforeAndAfterAll, FunSuite} import scodec.bits.ByteVector @@ -38,10 +38,6 @@ import scodec.bits.ByteVector class HtlcGenerationSpec extends FunSuite with BeforeAndAfterAll { - override def beforeAll { - Globals.blockCount.set(HtlcGenerationSpec.currentBlockCount) - } - test("compute fees") { val feeBaseMsat = 150000 msat val feeProportionalMillionth = 4L 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 e44b8d8a05..b142e521f5 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 @@ -25,7 +25,7 @@ import fr.acinq.eclair.channel.{CMD_FAIL_HTLC, CMD_FULFILL_HTLC} import fr.acinq.eclair.payment.PaymentLifecycle.ReceivePayment import fr.acinq.eclair.payment.PaymentRequest.ExtraHop import fr.acinq.eclair.wire.{IncorrectOrUnknownPaymentDetails, UpdateAddHtlc} -import fr.acinq.eclair.{CltvExpiryDelta, Globals, LongToBtcAmount, ShortChannelId, TestConstants, randomKey} +import fr.acinq.eclair.{CltvExpiryDelta, LongToBtcAmount, ShortChannelId, TestConstants, randomKey} import org.scalatest.FunSuiteLike import scodec.bits.ByteVector @@ -45,7 +45,7 @@ class PaymentHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLike system.eventStream.subscribe(eventListener.ref, classOf[PaymentReceived]) val amountMsat = 42000 msat - val expiry = CltvExpiryDelta(12).toCltvExpiry + val expiry = CltvExpiryDelta(12).toCltvExpiry(nodeParams.currentBlockHeight) { sender.send(handler, ReceivePayment(Some(amountMsat), "1 coffee")) @@ -81,9 +81,9 @@ class PaymentHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLike val pr = sender.expectMsgType[PaymentRequest] assert(nodeParams.db.payments.getIncomingPayment(pr.paymentHash).isEmpty) - val add = UpdateAddHtlc(ByteVector32(ByteVector.fill(32)(1)), 0, amountMsat, pr.paymentHash, cltvExpiry = CltvExpiryDelta(3).toCltvExpiry, TestConstants.emptyOnionPacket) + val add = UpdateAddHtlc(ByteVector32(ByteVector.fill(32)(1)), 0, amountMsat, pr.paymentHash, cltvExpiry = CltvExpiryDelta(3).toCltvExpiry(nodeParams.currentBlockHeight), TestConstants.emptyOnionPacket) sender.send(handler, add) - assert(sender.expectMsgType[CMD_FAIL_HTLC].reason == Right(IncorrectOrUnknownPaymentDetails(amountMsat, Globals.blockCount.get()))) + assert(sender.expectMsgType[CMD_FAIL_HTLC].reason == Right(IncorrectOrUnknownPaymentDetails(amountMsat, nodeParams.currentBlockHeight))) eventListener.expectNoMsg(300 milliseconds) assert(nodeParams.db.payments.getIncomingPayment(pr.paymentHash).isEmpty) } @@ -159,7 +159,7 @@ class PaymentHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLike system.eventStream.subscribe(eventListener.ref, classOf[PaymentReceived]) val amountMsat = 42000 msat - val expiry = CltvExpiryDelta(12).toCltvExpiry + val expiry = CltvExpiryDelta(12).toCltvExpiry(nodeParams.currentBlockHeight) sender.send(handler, ReceivePayment(Some(amountMsat), "some desc", expirySeconds_opt = Some(0))) val pr = sender.expectMsgType[PaymentRequest] diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala index 957dbdef11..7dcd6417e0 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala @@ -45,7 +45,7 @@ import scodec.bits.HexStringSyntax class PaymentLifecycleSpec extends BaseRouterSpec { val defaultAmountMsat = 142000000 msat - val defaultExpiry = Channel.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry + val defaultExpiryDelta = Channel.MIN_CLTV_EXPIRY_DELTA test("send to route") { fixture => import fixture._ @@ -63,7 +63,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { val CurrentState(_, WAITING_FOR_REQUEST) = monitor.expectMsgClass(classOf[CurrentState[_]]) // pre-computed route going from A to D - val request = SendPaymentToRoute(defaultPaymentHash, Seq(a, b, c, d), FinalLegacyPayload(defaultAmountMsat, defaultExpiry)) + val request = SendPaymentToRoute(defaultPaymentHash, Seq(a, b, c, d), FinalLegacyPayload(defaultAmountMsat, defaultExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight))) sender.send(paymentFSM, request) val Transition(_, WAITING_FOR_REQUEST, WAITING_FOR_ROUTE) = monitor.expectMsgClass(classOf[Transition[_]]) @@ -89,7 +89,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { paymentFSM ! SubscribeTransitionCallBack(monitor.ref) val CurrentState(_, WAITING_FOR_REQUEST) = monitor.expectMsgClass(classOf[CurrentState[_]]) - val request = SendPayment(defaultPaymentHash, f, FinalLegacyPayload(defaultAmountMsat, defaultExpiry), maxAttempts = 5) + val request = SendPayment(defaultPaymentHash, f, FinalLegacyPayload(defaultAmountMsat, defaultExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight)), maxAttempts = 5) sender.send(paymentFSM, request) val Transition(_, WAITING_FOR_REQUEST, WAITING_FOR_ROUTE) = monitor.expectMsgClass(classOf[Transition[_]]) val routeRequest = routerForwarder.expectMsgType[RouteRequest] @@ -112,7 +112,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { paymentFSM ! SubscribeTransitionCallBack(monitor.ref) val CurrentState(_, WAITING_FOR_REQUEST) = monitor.expectMsgClass(classOf[CurrentState[_]]) - val request = SendPayment(randomBytes32, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiry), maxAttempts = 5, routeParams = Some(RouteParams(randomize = false, maxFeeBase = 100 msat, maxFeePct = 0.0, routeMaxLength = 20, routeMaxCltv = CltvExpiryDelta(2016), ratios = None))) + val request = SendPayment(randomBytes32, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight)), maxAttempts = 5, routeParams = Some(RouteParams(randomize = false, maxFeeBase = 100 msat, maxFeePct = 0.0, routeMaxLength = 20, routeMaxCltv = CltvExpiryDelta(2016), ratios = None))) sender.send(paymentFSM, request) val Transition(_, WAITING_FOR_REQUEST, WAITING_FOR_ROUTE) = monitor.expectMsgClass(classOf[Transition[_]]) @@ -135,7 +135,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { paymentFSM ! SubscribeTransitionCallBack(monitor.ref) val CurrentState(_, WAITING_FOR_REQUEST) = monitor.expectMsgClass(classOf[CurrentState[_]]) - val request = SendPayment(defaultPaymentHash, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiry), maxAttempts = 2) + val request = SendPayment(defaultPaymentHash, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight)), maxAttempts = 2) sender.send(paymentFSM, request) awaitCond(paymentFSM.stateName == WAITING_FOR_ROUTE && paymentDb.getOutgoingPayment(id).exists(_.status == OutgoingPaymentStatus.PENDING)) @@ -178,7 +178,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { paymentFSM ! SubscribeTransitionCallBack(monitor.ref) val CurrentState(_, WAITING_FOR_REQUEST) = monitor.expectMsgClass(classOf[CurrentState[_]]) - val request = SendPayment(randomBytes32, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiry), maxAttempts = 2) + val request = SendPayment(randomBytes32, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight)), maxAttempts = 2) sender.send(paymentFSM, request) awaitCond(paymentFSM.stateName == WAITING_FOR_ROUTE && paymentDb.getOutgoingPayment(id).exists(_.status == OutgoingPaymentStatus.PENDING)) @@ -211,7 +211,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { paymentFSM ! SubscribeTransitionCallBack(monitor.ref) val CurrentState(_, WAITING_FOR_REQUEST) = monitor.expectMsgClass(classOf[CurrentState[_]]) - val request = SendPayment(defaultPaymentHash, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiry), maxAttempts = 2) + val request = SendPayment(defaultPaymentHash, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight)), maxAttempts = 2) sender.send(paymentFSM, request) awaitCond(paymentFSM.stateName == WAITING_FOR_ROUTE && paymentDb.getOutgoingPayment(id).exists(_.status == OutgoingPaymentStatus.PENDING)) @@ -242,7 +242,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { paymentFSM ! SubscribeTransitionCallBack(monitor.ref) val CurrentState(_, WAITING_FOR_REQUEST) = monitor.expectMsgClass(classOf[CurrentState[_]]) - val request = SendPayment(randomBytes32, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiry), maxAttempts = 2) + val request = SendPayment(randomBytes32, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight)), maxAttempts = 2) sender.send(paymentFSM, request) awaitCond(paymentFSM.stateName == WAITING_FOR_ROUTE) val WaitingForRoute(_, _, Nil) = paymentFSM.stateData @@ -282,7 +282,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { paymentFSM ! SubscribeTransitionCallBack(monitor.ref) val CurrentState(_, WAITING_FOR_REQUEST) = monitor.expectMsgClass(classOf[CurrentState[_]]) - val request = SendPayment(randomBytes32, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiry), maxAttempts = 5) + val request = SendPayment(randomBytes32, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight)), maxAttempts = 5) sender.send(paymentFSM, request) awaitCond(paymentFSM.stateName == WAITING_FOR_ROUTE && paymentDb.getOutgoingPayment(id).exists(_.status == OutgoingPaymentStatus.PENDING)) @@ -344,7 +344,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { paymentFSM ! SubscribeTransitionCallBack(monitor.ref) val CurrentState(_, WAITING_FOR_REQUEST) = monitor.expectMsgClass(classOf[CurrentState[_]]) - val request = SendPayment(randomBytes32, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiry), maxAttempts = 2) + val request = SendPayment(randomBytes32, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight)), maxAttempts = 2) sender.send(paymentFSM, request) awaitCond(paymentFSM.stateName == WAITING_FOR_ROUTE && paymentDb.getOutgoingPayment(id).exists(_.status == OutgoingPaymentStatus.PENDING)) @@ -392,7 +392,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { paymentFSM ! SubscribeTransitionCallBack(monitor.ref) val CurrentState(_, WAITING_FOR_REQUEST) = monitor.expectMsgClass(classOf[CurrentState[_]]) - val request = SendPayment(defaultPaymentHash, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiry), maxAttempts = 5) + val request = SendPayment(defaultPaymentHash, d, FinalLegacyPayload(defaultAmountMsat, defaultExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight)), maxAttempts = 5) sender.send(paymentFSM, request) val Transition(_, WAITING_FOR_REQUEST, WAITING_FOR_ROUTE) = monitor.expectMsgClass(classOf[Transition[_]]) val Transition(_, WAITING_FOR_ROUTE, WAITING_FOR_PAYMENT_COMPLETE) = monitor.expectMsgClass(classOf[Transition[_]]) @@ -442,7 +442,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { val CurrentState(_, WAITING_FOR_REQUEST) = monitor.expectMsgClass(classOf[CurrentState[_]]) // we send a payment to G which is just after the - val request = SendPayment(defaultPaymentHash, g, FinalLegacyPayload(defaultAmountMsat, defaultExpiry), maxAttempts = 5) + val request = SendPayment(defaultPaymentHash, g, FinalLegacyPayload(defaultAmountMsat, defaultExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight)), maxAttempts = 5) sender.send(paymentFSM, request) // the route will be A -> B -> G where B -> G has a channel_update with fees=0 diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/RouteCalculationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/RouteCalculationSpec.scala index ec467dad57..f55117a88e 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/RouteCalculationSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/RouteCalculationSpec.scala @@ -21,10 +21,10 @@ import fr.acinq.bitcoin.{Block, ByteVector32, ByteVector64, Satoshi} import fr.acinq.eclair.payment.PaymentRequest.ExtraHop import fr.acinq.eclair.router.Graph.GraphStructure.DirectedGraph.graphEdgeToHop import fr.acinq.eclair.router.Graph.GraphStructure.{DirectedGraph, GraphEdge} -import fr.acinq.eclair.router.Graph.{RichWeight, RoutingHeuristics, WeightRatios} +import fr.acinq.eclair.router.Graph.{RichWeight, WeightRatios} import fr.acinq.eclair.transactions.Transactions import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{CltvExpiryDelta, Globals, LongToBtcAmount, MilliSatoshi, ShortChannelId, ToMilliSatoshiConversion, randomKey} +import fr.acinq.eclair.{CltvExpiryDelta, LongToBtcAmount, MilliSatoshi, ShortChannelId, ToMilliSatoshiConversion, randomKey} import org.scalatest.FunSuite import scodec.bits._ @@ -51,7 +51,7 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - val route = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route.map(hops2Ids) === Success(1 :: 2 :: 3 :: 4 :: Nil)) } @@ -74,7 +74,7 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - val route = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS.copy(maxFeeBase = 1 msat)) + val route = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS.copy(maxFeeBase = 1 msat), currentBlockHeight = 400000) assert(route.map(hops2Ids) === Success(1 :: 2 :: 3 :: 4 :: Nil)) } @@ -111,7 +111,7 @@ class RouteCalculationSpec extends FunSuite { val graph = makeGraph(updates) - val Success(route) = Router.findRoute(graph, a, d, amount, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val Success(route) = Router.findRoute(graph, a, d, amount, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) val totalCost = Graph.pathWeight(hops2Edges(route), amount, isPartial = false, 0, None).cost @@ -122,7 +122,7 @@ class RouteCalculationSpec extends FunSuite { val (desc, update) = makeUpdate(5L, e, f, feeBase = 1 msat, feeProportionalMillionth = 400, minHtlc = 0 msat, maxHtlc = Some(10005 msat)) val graph1 = graph.addEdge(desc, update) - val Success(route1) = Router.findRoute(graph1, a, d, amount, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val Success(route1) = Router.findRoute(graph1, a, d, amount, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(hops2Ids(route1) === 1 :: 2 :: 3 :: Nil) } @@ -137,7 +137,7 @@ class RouteCalculationSpec extends FunSuite { ).toMap val g = makeGraph(updates) - val route = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route.map(hops2Ids) === Success(2 :: 5 :: Nil)) } @@ -152,11 +152,11 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - val route1 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route1 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route1.map(hops2Ids) === Success(1 :: 2 :: 3 :: 4 :: Nil)) val graphWithRemovedEdge = g.removeEdge(ChannelDesc(ShortChannelId(3L), c, d)) - val route2 = Router.findRoute(graphWithRemovedEdge, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route2 = Router.findRoute(graphWithRemovedEdge, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route2.map(hops2Ids) === Failure(RouteNotFound)) } @@ -177,7 +177,7 @@ class RouteCalculationSpec extends FunSuite { val graph = makeGraph(updates) - val route = Router.findRoute(graph, f, i, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route = Router.findRoute(graph, f, i, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route.map(hops2Ids) === Success(4 :: 3 :: Nil)) } @@ -199,7 +199,7 @@ class RouteCalculationSpec extends FunSuite { val graph = makeGraph(updates) - val route = Router.findRoute(graph, f, i, DEFAULT_AMOUNT_MSAT, numRoutes = 2, routeParams = DEFAULT_ROUTE_PARAMS) + val route = Router.findRoute(graph, f, i, DEFAULT_AMOUNT_MSAT, numRoutes = 2, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route.map(hops2Ids) === Success(4 :: Nil)) } @@ -220,7 +220,7 @@ class RouteCalculationSpec extends FunSuite { val graph = makeGraph(updates) - val route = Router.findRoute(graph, f, i, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route = Router.findRoute(graph, f, i, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route.map(hops2Ids) == Success(1 :: 2 :: 3 :: Nil)) } @@ -241,7 +241,7 @@ class RouteCalculationSpec extends FunSuite { val graph = makeGraph(updates) - val route = Router.findRoute(graph, f, i, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route = Router.findRoute(graph, f, i, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route.map(hops2Ids) === Failure(RouteNotFound)) } @@ -262,7 +262,7 @@ class RouteCalculationSpec extends FunSuite { val graph = makeGraph(updates) - val route = Router.findRoute(graph, f, i, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route = Router.findRoute(graph, f, i, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route.map(hops2Ids) === Success(1 :: 6 :: 3 :: Nil)) } @@ -277,7 +277,7 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - val route = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route.map(hops2Ids) === Success(1 :: 2 :: 3 :: 4 :: Nil)) } @@ -289,7 +289,7 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - val route = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route.map(hops2Ids) === Failure(RouteNotFound)) } @@ -302,7 +302,7 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - val route = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route.map(hops2Ids) === Failure(RouteNotFound)) } @@ -314,8 +314,8 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates).addVertex(a).addVertex(e) - assert(Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) === Failure(RouteNotFound)) - assert(Router.findRoute(g, b, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) === Failure(RouteNotFound)) + assert(Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) === Failure(RouteNotFound)) + assert(Router.findRoute(g, b, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) === Failure(RouteNotFound)) } test("route not found (amount too high OR too low)") { @@ -337,8 +337,8 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updatesHi) val g1 = makeGraph(updatesLo) - assert(Router.findRoute(g, a, d, highAmount, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) === Failure(RouteNotFound)) - assert(Router.findRoute(g1, a, d, lowAmount, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) === Failure(RouteNotFound)) + assert(Router.findRoute(g, a, d, highAmount, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) === Failure(RouteNotFound)) + assert(Router.findRoute(g1, a, d, lowAmount, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) === Failure(RouteNotFound)) } test("route to self") { @@ -350,7 +350,7 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - val route = Router.findRoute(g, a, a, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route = Router.findRoute(g, a, a, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route.map(hops2Ids) === Failure(CannotRouteToSelf)) } @@ -364,7 +364,7 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - val route = Router.findRoute(g, a, b, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route = Router.findRoute(g, a, b, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route.map(hops2Ids) === Success(1 :: Nil)) } @@ -380,10 +380,10 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - val route1 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route1 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route1.map(hops2Ids) === Success(1 :: 2 :: 3 :: 4 :: Nil)) - val route2 = Router.findRoute(g, e, a, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route2 = Router.findRoute(g, e, a, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route2.map(hops2Ids) === Failure(RouteNotFound)) } @@ -412,7 +412,7 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - val hops = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS).get + val hops = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000).get assert(hops === Hop(a, b, uab) :: Hop(b, c, ubc) :: Hop(c, d, ucd) :: Hop(d, e, ude) :: Nil) } @@ -449,7 +449,7 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - val route1 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, ignoredEdges = Set(ChannelDesc(ShortChannelId(3L), c, d)), routeParams = DEFAULT_ROUTE_PARAMS) + val route1 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, ignoredEdges = Set(ChannelDesc(ShortChannelId(3L), c, d)), routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route1.map(hops2Ids) === Failure(RouteNotFound)) // verify that we left the graph untouched @@ -458,7 +458,7 @@ class RouteCalculationSpec extends FunSuite { assert(g.containsVertex(d)) // make sure we can find a route if without the blacklist - val route2 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route2 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route2.map(hops2Ids) === Success(1 :: 2 :: 3 :: 4 :: Nil)) } @@ -471,14 +471,14 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - val route = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route.map(hops2Ids) === Failure(RouteNotFound)) // now we add the missing edge to reach the destination val (extraDesc, extraUpdate) = makeUpdate(4L, d, e, 5 msat, 5) val extraGraphEdges = Set(GraphEdge(extraDesc, extraUpdate)) - val route1 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, extraEdges = extraGraphEdges, routeParams = DEFAULT_ROUTE_PARAMS) + val route1 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, extraEdges = extraGraphEdges, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route1.map(hops2Ids) === Success(1 :: 2 :: 3 :: 4 :: Nil)) } @@ -492,7 +492,7 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - val route1 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route1 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route1.map(hops2Ids) === Success(1 :: 2 :: 3 :: 4 :: Nil)) assert(route1.get(1).lastUpdate.feeBaseMsat === 10.msat) @@ -500,7 +500,7 @@ class RouteCalculationSpec extends FunSuite { val extraGraphEdges = Set(GraphEdge(extraDesc, extraUpdate)) - val route2 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, extraEdges = extraGraphEdges, routeParams = DEFAULT_ROUTE_PARAMS) + val route2 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, extraEdges = extraGraphEdges, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route2.map(hops2Ids) === Success(1 :: 2 :: 3 :: 4 :: Nil)) assert(route2.get(1).lastUpdate.feeBaseMsat === 5.msat) } @@ -560,10 +560,10 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - assert(Router.findRoute(g, nodes(0), nodes(18), DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS).map(hops2Ids) === Success(0 until 18)) - assert(Router.findRoute(g, nodes(0), nodes(19), DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS).map(hops2Ids) === Success(0 until 19)) - assert(Router.findRoute(g, nodes(0), nodes(20), DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS).map(hops2Ids) === Success(0 until 20)) - assert(Router.findRoute(g, nodes(0), nodes(21), DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS).map(hops2Ids) === Failure(RouteNotFound)) + assert(Router.findRoute(g, nodes(0), nodes(18), DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000).map(hops2Ids) === Success(0 until 18)) + assert(Router.findRoute(g, nodes(0), nodes(19), DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000).map(hops2Ids) === Success(0 until 19)) + assert(Router.findRoute(g, nodes(0), nodes(20), DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000).map(hops2Ids) === Success(0 until 20)) + assert(Router.findRoute(g, nodes(0), nodes(21), DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000).map(hops2Ids) === Failure(RouteNotFound)) } test("ignore cheaper route when it has more than 20 hops") { @@ -579,7 +579,7 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates2) - val route = Router.findRoute(g, nodes(0), nodes(49), DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route = Router.findRoute(g, nodes(0), nodes(49), DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route.map(hops2Ids) === Success(0 :: 1 :: 99 :: 48 :: Nil)) } @@ -595,7 +595,7 @@ class RouteCalculationSpec extends FunSuite { makeUpdate(6, f, d, feeBase = 5 msat, 0, minHtlc = 0 msat, maxHtlc = None, CltvExpiryDelta(9)) ).toMap) - val route = Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS.copy(routeMaxCltv = CltvExpiryDelta(28))) + val route = Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS.copy(routeMaxCltv = CltvExpiryDelta(28)), currentBlockHeight = 400000) assert(route.map(hops2Ids) === Success(4 :: 5 :: 6 :: Nil)) } @@ -611,7 +611,7 @@ class RouteCalculationSpec extends FunSuite { makeUpdate(6, b, f, feeBase = 5 msat, 0, minHtlc = 0 msat, maxHtlc = None, CltvExpiryDelta(9)) ).toMap) - val route = Router.findRoute(g, a, f, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS.copy(routeMaxLength = 3)) + val route = Router.findRoute(g, a, f, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS.copy(routeMaxLength = 3), currentBlockHeight = 400000) assert(route.map(hops2Ids) === Success(1 :: 6 :: Nil)) } @@ -626,7 +626,7 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - val route1 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route1 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route1.map(hops2Ids) === Success(1 :: 2 :: 4 :: 5 :: Nil)) } @@ -644,7 +644,7 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - val route1 = Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val route1 = Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(route1.map(hops2Ids) === Success(1 :: 3 :: 5 :: Nil)) } @@ -772,7 +772,7 @@ class RouteCalculationSpec extends FunSuite { makeUpdate(7L, e, c, feeBase = 9 msat, 0) ).toMap) - (for {_ <- 0 to 10} yield Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT, numRoutes = 3, routeParams = strictFeeParams)).map { + (for {_ <- 0 to 10} yield Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT, numRoutes = 3, routeParams = strictFeeParams, currentBlockHeight = 400000)).map { case Failure(thr) => fail(thr) case Success(someRoute) => @@ -801,14 +801,14 @@ class RouteCalculationSpec extends FunSuite { val g = makeGraph(updates) - val Success(routeFeeOptimized) = Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT, numRoutes = 0, routeParams = DEFAULT_ROUTE_PARAMS) + val Success(routeFeeOptimized) = Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT, numRoutes = 0, routeParams = DEFAULT_ROUTE_PARAMS, currentBlockHeight = 400000) assert(hops2Nodes(routeFeeOptimized) === (a, b) :: (b, c) :: (c, d) :: Nil) val Success(routeCltvOptimized) = Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT, numRoutes = 0, routeParams = DEFAULT_ROUTE_PARAMS.copy(ratios = Some(WeightRatios( cltvDeltaFactor = 1, ageFactor = 0, capacityFactor = 0 - )))) + ))), currentBlockHeight = 400000) assert(hops2Nodes(routeCltvOptimized) === (a, e) :: (e, f) :: (f, d) :: Nil) @@ -816,7 +816,7 @@ class RouteCalculationSpec extends FunSuite { cltvDeltaFactor = 0, ageFactor = 0, capacityFactor = 1 - )))) + ))), currentBlockHeight = 400000) assert(hops2Nodes(routeCapacityOptimized) === (a, e) :: (e, c) :: (c, d) :: Nil) } @@ -833,13 +833,11 @@ class RouteCalculationSpec extends FunSuite { makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x6"), f, d, feeBase = 1 msat, 0, minHtlc = 0 msat, maxHtlc = None, cltvDelta = CltvExpiryDelta(144)) ).toMap) - Globals.blockCount.set(currentBlockHeight) - val Success(routeScoreOptimized) = Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT / 2, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS.copy(ratios = Some(WeightRatios( ageFactor = 0.33, cltvDeltaFactor = 0.33, capacityFactor = 0.33 - )))) + ))), currentBlockHeight = currentBlockHeight) assert(hops2Nodes(routeScoreOptimized) === (a, b) :: (b, c) :: (c, d) :: Nil) } @@ -858,7 +856,7 @@ class RouteCalculationSpec extends FunSuite { ageFactor = 0.33, cltvDeltaFactor = 0.33, capacityFactor = 0.33 - )))) + ))), currentBlockHeight = 400000) assert(hops2Nodes(routeScoreOptimized) === (a, b) :: (b, c) :: (c, d) :: Nil) } @@ -879,7 +877,7 @@ class RouteCalculationSpec extends FunSuite { ageFactor = 0.33, cltvDeltaFactor = 0.33, capacityFactor = 0.33 - )))) + ))), currentBlockHeight = 400000) assert(hops2Nodes(routeScoreOptimized) === (a, e) :: (e, f) :: (f, d) :: Nil) } @@ -920,8 +918,7 @@ class RouteCalculationSpec extends FunSuite { val targetNode = PublicKey(hex"024655b768ef40951b20053a5c4b951606d4d86085d51238f2c67c7dec29c792ca") val amount = 351000 msat - Globals.blockCount.set(567634) // simulate mainnet block for heuristic - val Success(route) = Router.findRoute(g, thisNode, targetNode, amount, 1, Set.empty, Set.empty, Set.empty, params) + val Success(route) = Router.findRoute(g, thisNode, targetNode, amount, 1, Set.empty, Set.empty, Set.empty, params, currentBlockHeight = 567634) // simulate mainnet block for heuristic assert(route.size == 2) assert(route.last.nextNodeId == targetNode) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala index f3785dd226..3405475651 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala @@ -30,7 +30,7 @@ import fr.acinq.eclair.router.Announcements.makeChannelUpdate import fr.acinq.eclair.router.RouteCalculationSpec.DEFAULT_AMOUNT_MSAT import fr.acinq.eclair.transactions.Scripts import fr.acinq.eclair.wire.QueryShortChannelIds -import fr.acinq.eclair.{CltvExpiryDelta, Globals, LongToBtcAmount, ShortChannelId, randomKey} +import fr.acinq.eclair.{CltvExpiryDelta, LongToBtcAmount, ShortChannelId, randomKey} import scodec.bits._ import scala.compat.Platform @@ -269,7 +269,7 @@ class RouterSpec extends BaseRouterSpec { test("ask for channels that we marked as stale for which we receive a new update") { fixture => import fixture._ - val blockHeight = Globals.blockCount.get().toInt - 2020 + val blockHeight = 400000 - 2020 val channelId = ShortChannelId(blockHeight, 5, 0) val announcement = channelAnnouncement(channelId, priv_a, priv_c, priv_funding_a, priv_funding_c) val timestamp = (Platform.currentTime.milliseconds - 14.days - 1.day).toSeconds diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/RoutingSyncSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/RoutingSyncSpec.scala index 9b1f6f24b4..71759368c9 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/RoutingSyncSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/RoutingSyncSpec.scala @@ -37,6 +37,7 @@ import scala.collection.{SortedSet, immutable, mutable} import scala.compat.Platform import scala.concurrent.duration._ + class RoutingSyncSpec extends TestKit(ActorSystem("test")) with FunSuiteLike { import RoutingSyncSpec._ 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 d02ce4c1d0..d88099179b 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 @@ -84,6 +84,7 @@ class TransactionsSpec extends FunSuite with Logging { val localDustLimit = 546 sat val toLocalDelay = CltvExpiryDelta(144) val feeratePerKw = fr.acinq.eclair.MinimumFeeratePerKw + val blockHeight = 400000 { // ClaimP2WPKHOutputTx @@ -125,7 +126,7 @@ 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 = randomBytes32 - val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, (20000 * 1000) msat, sha256(paymentPreimage), CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, (20000 * 1000) msat, sha256(paymentPreimage), CltvExpiryDelta(144).toCltvExpiry(blockHeight), TestConstants.emptyOnionPacket) 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(htlc.amountMsat.truncateToSatoshi, pubKeyScript) :: Nil, lockTime = 0) @@ -140,7 +141,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 = randomBytes32 - val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, (20000 * 1000) msat, sha256(paymentPreimage), CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, (20000 * 1000) msat, sha256(paymentPreimage), CltvExpiryDelta(144).toCltvExpiry(blockHeight), TestConstants.emptyOnionPacket) val pubKeyScript = write(pay2wsh(htlcOffered(localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localRevocationPriv.publicKey, ripemd160(htlc.paymentHash)))) val commitTx = Transaction(version = 0, txIn = Nil, txOut = TxOut(htlc.amountMsat.truncateToSatoshi, pubKeyScript) :: Nil, lockTime = 0) val claimHtlcSuccessTx = makeClaimHtlcSuccessTx(commitTx, outputsAlreadyUsed = Set.empty, localDustLimit, remoteHtlcPriv.publicKey, localHtlcPriv.publicKey, localRevocationPriv.publicKey, finalPubKeyScript, htlc, feeratePerKw) @@ -154,7 +155,7 @@ 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 = randomBytes32 - val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, (20000 * 1000) msat, sha256(paymentPreimage), CltvExpiryDelta(144).toCltvExpiry, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, (20000 * 1000) msat, sha256(paymentPreimage), CltvExpiryDelta(144).toCltvExpiry(blockHeight), TestConstants.emptyOnionPacket) 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(htlc.amountMsat.truncateToSatoshi, pubKeyScript) :: Nil, lockTime = 0) val claimClaimHtlcTimeoutTx = makeClaimHtlcTimeoutTx(commitTx, outputsAlreadyUsed = Set.empty, localDustLimit, remoteHtlcPriv.publicKey, localHtlcPriv.publicKey, localRevocationPriv.publicKey, finalPubKeyScript, htlc, feeratePerKw) diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/Handlers.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/Handlers.scala index b11aeb5888..9d3b690064 100644 --- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/Handlers.scala +++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/Handlers.scala @@ -115,4 +115,18 @@ class Handlers(fKit: Future[Kit])(implicit ec: ExecutionContext = ExecutionConte def notification(title: String, message: String, notificationType: NotificationType = NOTIFICATION_NONE, showAppName: Boolean = true): Unit = { notifsController.foreach(_.addNotification(if (showAppName) s"Eclair - $title" else title, message, notificationType)) } + + /** + * Retrieves on-chain fees for a funding transaction, using the funding block target set in the config file. + * + * @return Future containing a Long in satoshi per kilobyte + */ + def getFundingFeeRatePerKb(): Future[Long] = { + for { + kit <- fKit + ratePerKw = { + kit.nodeParams.onChainFeeConf.feeEstimator.getFeeratePerKb(target = kit.nodeParams.onChainFeeConf.feeTargets.fundingBlockTarget) + } + } yield ratePerKw + } } diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/OpenChannelController.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/OpenChannelController.scala index c038671808..379c94bfbd 100644 --- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/OpenChannelController.scala +++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/OpenChannelController.scala @@ -25,7 +25,7 @@ import fr.acinq.eclair.channel.{Channel, ChannelFlags} import fr.acinq.eclair.gui.utils.Constants import fr.acinq.eclair.gui.{FxApp, Handlers} import fr.acinq.eclair.io.{NodeURI, Peer} -import fr.acinq.eclair.{CoinUtils, Globals, MilliSatoshi} +import fr.acinq.eclair.{CoinUtils, MilliSatoshi} import grizzled.slf4j.Logging import javafx.beans.value.{ChangeListener, ObservableValue} import javafx.event.ActionEvent @@ -33,6 +33,7 @@ import javafx.fxml.FXML import javafx.scene.control._ import javafx.stage.Stage +import scala.concurrent.ExecutionContext import scala.concurrent.duration._ import scala.util.{Failure, Success, Try} @@ -56,7 +57,16 @@ class OpenChannelController(val handlers: Handlers, val stage: Stage) extends Lo @FXML def initialize() = { fundingUnit.setItems(Constants.FX_UNITS_ARRAY_NO_MSAT) fundingUnit.setValue(FxApp.getUnit.label) - feerateField.setText((Globals.feeratesPerKB.get().blocks_6 / 1000).toString) + + handlers.getFundingFeeRatePerKb().onComplete { + case Success(feeSatKb) => + feerateField.setText((feeSatKb / 1000).toString) + feerateError.setText("") + case Failure(t) => + logger.error(s"error when estimating funding fee from GUI: ${t.getLocalizedMessage}") + feerateField.setText("") + feerateError.setText("Could not estimate fees.") + } (ExecutionContext.Implicits.global) simpleConnection.selectedProperty.addListener(new ChangeListener[Boolean] { override def changed(observable: ObservableValue[_ <: Boolean], oldValue: Boolean, newValue: Boolean) = { diff --git a/pom.xml b/pom.xml index 161a4adb19..10c833307c 100644 --- a/pom.xml +++ b/pom.xml @@ -222,8 +222,7 @@ ${project.build.directory} - -Xmx1024m - -Dfile.encoding=UTF-8 + -Xmx1024m -Dfile.encoding=UTF-8