mirror of
https://github.com/ACINQ/eclair.git
synced 2025-02-22 14:22:39 +01:00
Removed Globals class (#1127)
This is a prerequisite to parallelization of tests.
This commit is contained in:
parent
26e4432c51
commit
2fbf46a344
45 changed files with 429 additions and 419 deletions
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
@ -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,
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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] {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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")
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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._
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) = {
|
||||
|
|
3
pom.xml
3
pom.xml
|
@ -222,8 +222,7 @@
|
|||
<systemProperties>
|
||||
<buildDirectory>${project.build.directory}</buildDirectory>
|
||||
</systemProperties>
|
||||
<argLine>-Xmx1024m</argLine>
|
||||
<argLine>-Dfile.encoding=UTF-8</argLine>
|
||||
<argLine>-Xmx1024m -Dfile.encoding=UTF-8</argLine>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
|
|
Loading…
Add table
Reference in a new issue