diff --git a/eclair-core/eclair-cli b/eclair-core/eclair-cli index 89a05cdde7..be2102d71c 100755 --- a/eclair-core/eclair-cli +++ b/eclair-core/eclair-cli @@ -89,7 +89,7 @@ jq_filter='if type=="object" and .error != null then .error else .'; # apply special jq filter if we are in "short" ouput mode -- only for specific commands such as 'channels' if [ "$short" = true ]; then - jq_channel_filter="{ nodeId, shortChannelId: .data.shortChannelId, channelId, state, balanceSat: (try (.data.commitments.localCommit.spec.toLocalMsat / 1000 | floor) catch null), capacitySat: .data.commitments.commitInput.amountSatoshis, channelPoint: .data.commitments.commitInput.outPoint }"; + jq_channel_filter="{ nodeId, shortChannelId: .data.shortChannelId, channelId, state, balanceSat: (try (.data.commitments.localCommit.spec.toLocal / 1000 | floor) catch null), capacitySat: .data.commitments.commitInput.amountSatoshis, channelPoint: .data.commitments.commitInput.outPoint }"; case $api_endpoint in "channels") jq_filter="$jq_filter | map( $jq_channel_filter )" ;; "channel") jq_filter="$jq_filter | $jq_channel_filter" ;; diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/CoinUtils.scala b/eclair-core/src/main/scala/fr/acinq/eclair/CoinUtils.scala index e4453ab8aa..2e58bef4dd 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/CoinUtils.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/CoinUtils.scala @@ -17,10 +17,8 @@ package fr.acinq.eclair import java.text.{DecimalFormat, NumberFormat} - -import fr.acinq.bitcoin.{Btc, BtcAmount, MilliBtc, MilliSatoshi, Satoshi} +import fr.acinq.bitcoin.{Btc, BtcAmount, MilliBtc, Satoshi} import grizzled.slf4j.Logging - import scala.util.{Failure, Success, Try} /** @@ -154,7 +152,7 @@ object CoinUtils extends Logging { } def convertStringAmountToSat(amount: String, unit: String): Satoshi = - fr.acinq.bitcoin.millisatoshi2satoshi(CoinUtils.convertStringAmountToMsat(amount, unit)) + CoinUtils.convertStringAmountToMsat(amount, unit).truncateToSatoshi /** * Only BtcUnit, MBtcUnit, BitUnit, SatUnit and MSatUnit codes or label are supported. @@ -223,6 +221,11 @@ object CoinUtils extends Logging { if (withUnit) s"$formatted ${unit.shortLabel}" else formatted } + def formatAmountInUnit(amount: MilliSatoshi, unit: CoinUnit, withUnit: Boolean): String = { + val formatted = COIN_FORMAT.format(rawAmountInUnit(amount, unit)) + if (withUnit) s"$formatted ${unit.shortLabel}" else formatted + } + /** * Converts the amount to the user preferred unit and returns the BigDecimal value. * This method is useful to feed numeric text input without formatting. @@ -237,7 +240,10 @@ object CoinUtils extends Logging { case a => throw new IllegalArgumentException(s"unhandled unit $a") }) match { case Success(b) => b - case Failure(t) => logger.error("can not convert amount to user unit", t) + case Failure(t) => + logger.error("can not convert amount to user unit", t) -1 } + + def rawAmountInUnit(msat: MilliSatoshi, unit: CoinUnit): BigDecimal = BigDecimal(msat.amount) / unit.factorToMsat } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala index e423806715..e22b621d35 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala @@ -22,7 +22,7 @@ import akka.actor.ActorRef import akka.pattern._ import akka.util.Timeout import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi, Satoshi} +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.eclair.channel.Register.{Forward, ForwardShortId} import fr.acinq.eclair.channel._ import fr.acinq.eclair.db.{IncomingPayment, NetworkFee, OutgoingPayment, Stats} @@ -60,13 +60,13 @@ trait Eclair { def disconnect(nodeId: PublicKey)(implicit timeout: Timeout): Future[String] - def open(nodeId: PublicKey, fundingSatoshis: Long, pushMsat_opt: Option[Long], fundingFeerateSatByte_opt: Option[Long], flags_opt: Option[Int], openTimeout_opt: Option[Timeout])(implicit timeout: Timeout): Future[String] + def open(nodeId: PublicKey, fundingAmount: Satoshi, pushAmount_opt: Option[MilliSatoshi], fundingFeerateSatByte_opt: Option[Long], flags_opt: Option[Int], openTimeout_opt: Option[Timeout])(implicit timeout: Timeout): Future[String] def close(channelIdentifier: Either[ByteVector32, ShortChannelId], scriptPubKey_opt: Option[ByteVector])(implicit timeout: Timeout): Future[String] def forceClose(channelIdentifier: Either[ByteVector32, ShortChannelId])(implicit timeout: Timeout): Future[String] - def updateRelayFee(channelIdentifier: Either[ByteVector32, ShortChannelId], feeBaseMsat: Long, feeProportionalMillionths: Long)(implicit timeout: Timeout): Future[String] + def updateRelayFee(channelIdentifier: Either[ByteVector32, ShortChannelId], feeBase: MilliSatoshi, feeProportionalMillionths: Long)(implicit timeout: Timeout): Future[String] def channelsInfo(toRemoteNode_opt: Option[PublicKey])(implicit timeout: Timeout): Future[Iterable[RES_GETINFO]] @@ -74,17 +74,17 @@ trait Eclair { def peersInfo()(implicit timeout: Timeout): Future[Iterable[PeerInfo]] - def receive(description: String, amountMsat_opt: Option[Long], expire_opt: Option[Long], fallbackAddress_opt: Option[String], paymentPreimage_opt: Option[ByteVector32])(implicit timeout: Timeout): Future[PaymentRequest] + def receive(description: String, amount_opt: Option[MilliSatoshi], expire_opt: Option[Long], fallbackAddress_opt: Option[String], paymentPreimage_opt: Option[ByteVector32])(implicit timeout: Timeout): Future[PaymentRequest] def receivedInfo(paymentHash: ByteVector32)(implicit timeout: Timeout): Future[Option[IncomingPayment]] - def send(recipientNodeId: PublicKey, amountMsat: Long, paymentHash: ByteVector32, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty, minFinalCltvExpiry_opt: Option[Long] = None, maxAttempts_opt: Option[Int] = None, feeThresholdSat_opt: Option[Long] = None, maxFeePct_opt: Option[Double] = None)(implicit timeout: Timeout): Future[UUID] + def send(recipientNodeId: PublicKey, amount: MilliSatoshi, paymentHash: ByteVector32, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty, minFinalCltvExpiry_opt: Option[Long] = None, maxAttempts_opt: Option[Int] = None, feeThresholdSat_opt: Option[Satoshi] = None, maxFeePct_opt: Option[Double] = None)(implicit timeout: Timeout): Future[UUID] def sentInfo(id: Either[UUID, ByteVector32])(implicit timeout: Timeout): Future[Seq[OutgoingPayment]] - def findRoute(targetNodeId: PublicKey, amountMsat: Long, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty)(implicit timeout: Timeout): Future[RouteResponse] + def findRoute(targetNodeId: PublicKey, amount: MilliSatoshi, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty)(implicit timeout: Timeout): Future[RouteResponse] - def sendToRoute(route: Seq[PublicKey], amountMsat: Long, paymentHash: ByteVector32, finalCltvExpiry: Long)(implicit timeout: Timeout): Future[UUID] + def sendToRoute(route: Seq[PublicKey], amount: MilliSatoshi, paymentHash: ByteVector32, finalCltvExpiry: Long)(implicit timeout: Timeout): Future[UUID] def audit(from_opt: Option[Long], to_opt: Option[Long])(implicit timeout: Timeout): Future[AuditResponse] @@ -122,13 +122,13 @@ class EclairImpl(appKit: Kit) extends Eclair { (appKit.switchboard ? Peer.Disconnect(nodeId)).mapTo[String] } - override def open(nodeId: PublicKey, fundingSatoshis: Long, pushMsat_opt: Option[Long], fundingFeerateSatByte_opt: Option[Long], flags_opt: Option[Int], openTimeout_opt: Option[Timeout])(implicit timeout: Timeout): Future[String] = { + override def open(nodeId: PublicKey, fundingAmount: Satoshi, pushAmount_opt: Option[MilliSatoshi], fundingFeerateSatByte_opt: Option[Long], flags_opt: Option[Int], openTimeout_opt: Option[Timeout])(implicit timeout: Timeout): Future[String] = { // we want the open timeout to expire *before* the default ask timeout, otherwise user won't get a generic response val openTimeout = openTimeout_opt.getOrElse(Timeout(10 seconds)) (appKit.switchboard ? Peer.OpenChannel( remoteNodeId = nodeId, - fundingSatoshis = Satoshi(fundingSatoshis), - pushMsat = pushMsat_opt.map(MilliSatoshi).getOrElse(MilliSatoshi(0)), + fundingSatoshis = fundingAmount, + pushMsat = pushAmount_opt.getOrElse(MilliSatoshi(0)), fundingTxFeeratePerKw_opt = fundingFeerateSatByte_opt.map(feerateByte2Kw), channelFlags = flags_opt.map(_.toByte), timeout_opt = Some(openTimeout))).mapTo[String] @@ -142,7 +142,7 @@ class EclairImpl(appKit: Kit) extends Eclair { sendToChannel(channelIdentifier, CMD_FORCECLOSE).mapTo[String] } - override def updateRelayFee(channelIdentifier: Either[ByteVector32, ShortChannelId], feeBaseMsat: Long, feeProportionalMillionths: Long)(implicit timeout: Timeout): Future[String] = { + override def updateRelayFee(channelIdentifier: Either[ByteVector32, ShortChannelId], feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long)(implicit timeout: Timeout): Future[String] = { sendToChannel(channelIdentifier, CMD_UPDATE_RELAY_FEE(feeBaseMsat, feeProportionalMillionths)).mapTo[String] } @@ -177,31 +177,31 @@ class EclairImpl(appKit: Kit) extends Eclair { case Some(pk) => (appKit.router ? 'updatesMap).mapTo[Map[ChannelDesc, ChannelUpdate]].map(_.filter(e => e._1.a == pk || e._1.b == pk).values) } - override def receive(description: String, amountMsat_opt: Option[Long], expire_opt: Option[Long], fallbackAddress_opt: Option[String], paymentPreimage_opt: Option[ByteVector32])(implicit timeout: Timeout): Future[PaymentRequest] = { + override def receive(description: String, amount_opt: Option[MilliSatoshi], expire_opt: Option[Long], fallbackAddress_opt: Option[String], paymentPreimage_opt: Option[ByteVector32])(implicit timeout: Timeout): Future[PaymentRequest] = { fallbackAddress_opt.map { fa => fr.acinq.eclair.addressToPublicKeyScript(fa, appKit.nodeParams.chainHash) } // if it's not a bitcoin address throws an exception - (appKit.paymentHandler ? ReceivePayment(description = description, amountMsat_opt = amountMsat_opt.map(MilliSatoshi), expirySeconds_opt = expire_opt, fallbackAddress = fallbackAddress_opt, paymentPreimage = paymentPreimage_opt)).mapTo[PaymentRequest] + (appKit.paymentHandler ? ReceivePayment(description = description, amount_opt = amount_opt, expirySeconds_opt = expire_opt, fallbackAddress = fallbackAddress_opt, paymentPreimage = paymentPreimage_opt)).mapTo[PaymentRequest] } - override def findRoute(targetNodeId: PublicKey, amountMsat: Long, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty)(implicit timeout: Timeout): Future[RouteResponse] = { - (appKit.router ? RouteRequest(appKit.nodeParams.nodeId, targetNodeId, amountMsat, assistedRoutes)).mapTo[RouteResponse] + override def findRoute(targetNodeId: PublicKey, amount: MilliSatoshi, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty)(implicit timeout: Timeout): Future[RouteResponse] = { + (appKit.router ? RouteRequest(appKit.nodeParams.nodeId, targetNodeId, amount, assistedRoutes)).mapTo[RouteResponse] } - override def sendToRoute(route: Seq[PublicKey], amountMsat: Long, paymentHash: ByteVector32, finalCltvExpiry: Long)(implicit timeout: Timeout): Future[UUID] = { - (appKit.paymentInitiator ? SendPaymentToRoute(amountMsat, paymentHash, route, finalCltvExpiry)).mapTo[UUID] + override def sendToRoute(route: Seq[PublicKey], amount: MilliSatoshi, paymentHash: ByteVector32, finalCltvExpiry: Long)(implicit timeout: Timeout): Future[UUID] = { + (appKit.paymentInitiator ? SendPaymentToRoute(amount, paymentHash, route, finalCltvExpiry)).mapTo[UUID] } - override def send(recipientNodeId: PublicKey, amountMsat: Long, paymentHash: ByteVector32, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty, minFinalCltvExpiry_opt: Option[Long], maxAttempts_opt: Option[Int], feeThresholdSat_opt: Option[Long], maxFeePct_opt: Option[Double])(implicit timeout: Timeout): Future[UUID] = { + override def send(recipientNodeId: PublicKey, amount: MilliSatoshi, paymentHash: ByteVector32, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty, minFinalCltvExpiry_opt: Option[Long], maxAttempts_opt: Option[Int], feeThreshold_opt: Option[Satoshi], maxFeePct_opt: Option[Double])(implicit timeout: Timeout): Future[UUID] = { val maxAttempts = maxAttempts_opt.getOrElse(appKit.nodeParams.maxPaymentAttempts) val defaultRouteParams = Router.getDefaultRouteParams(appKit.nodeParams.routerConf) val routeParams = defaultRouteParams.copy( maxFeePct = maxFeePct_opt.getOrElse(defaultRouteParams.maxFeePct), - maxFeeBaseMsat = feeThresholdSat_opt.map(_ * 1000).getOrElse(defaultRouteParams.maxFeeBaseMsat) + maxFeeBase = feeThreshold_opt.map(_.toMilliSatoshi).getOrElse(defaultRouteParams.maxFeeBase) ) val sendPayment = minFinalCltvExpiry_opt match { - case Some(minCltv) => SendPayment(amountMsat, paymentHash, recipientNodeId, assistedRoutes, finalCltvExpiry = minCltv, maxAttempts = maxAttempts, routeParams = Some(routeParams)) - case None => SendPayment(amountMsat, paymentHash, recipientNodeId, assistedRoutes, maxAttempts = maxAttempts, routeParams = Some(routeParams)) + case Some(minCltv) => SendPayment(amount, paymentHash, recipientNodeId, assistedRoutes, finalCltvExpiry = minCltv, maxAttempts = maxAttempts, routeParams = Some(routeParams)) + case None => SendPayment(amount, paymentHash, recipientNodeId, assistedRoutes, maxAttempts = maxAttempts, routeParams = Some(routeParams)) } (appKit.paymentInitiator ? sendPayment).mapTo[UUID] } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala b/eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala index 55f5fb2859..2daa66510f 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala @@ -23,8 +23,8 @@ import java.util.concurrent.TimeUnit import com.typesafe.config.{Config, ConfigFactory} import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{Block, ByteVector32} -import fr.acinq.eclair.NodeParams.{WatcherType} +import fr.acinq.bitcoin.{Block, ByteVector32, Satoshi} +import fr.acinq.eclair.NodeParams.WatcherType import fr.acinq.eclair.blockchain.fee.{FeeEstimator, FeeTargets, OnChainFeeConf} import fr.acinq.eclair.channel.Channel import fr.acinq.eclair.crypto.KeyManager @@ -33,7 +33,6 @@ import fr.acinq.eclair.router.RouterConf import fr.acinq.eclair.tor.Socks5ProxyParams import fr.acinq.eclair.wire.{Color, NodeAddress} import scodec.bits.ByteVector - import scala.collection.JavaConversions._ import scala.concurrent.duration.FiniteDuration @@ -47,17 +46,17 @@ case class NodeParams(keyManager: KeyManager, globalFeatures: ByteVector, localFeatures: ByteVector, overrideFeatures: Map[PublicKey, (ByteVector, ByteVector)], - dustLimitSatoshis: Long, + dustLimit: Satoshi, onChainFeeConf: OnChainFeeConf, maxHtlcValueInFlightMsat: UInt64, maxAcceptedHtlcs: Int, expiryDeltaBlocks: Int, fulfillSafetyBeforeTimeoutBlocks: Int, - htlcMinimumMsat: Int, + htlcMinimum: MilliSatoshi, toRemoteDelayBlocks: Int, maxToLocalDelayBlocks: Int, minDepthBlocks: Int, - feeBaseMsat: Int, + feeBase: MilliSatoshi, feeProportionalMillionth: Int, reserveToFundingRatio: Double, maxReserveToFundingRatio: Double, @@ -73,7 +72,7 @@ case class NodeParams(keyManager: KeyManager, channelFlags: Byte, watcherType: WatcherType, paymentRequestExpiry: FiniteDuration, - minFundingSatoshis: Long, + minFundingSatoshis: Satoshi, routerConf: RouterConf, socksProxy_opt: Option[Socks5ProxyParams], maxPaymentAttempts: Int) { @@ -137,7 +136,7 @@ object NodeParams { case _ => BITCOIND } - val dustLimitSatoshis = config.getLong("dust-limit-satoshis") + val dustLimitSatoshis = Satoshi(config.getLong("dust-limit-satoshis")) if (chainHash == Block.LivenetGenesisBlock.hash) { require(dustLimitSatoshis >= Channel.MIN_DUSTLIMIT, s"dust limit must be greater than ${Channel.MIN_DUSTLIMIT}") } @@ -195,7 +194,7 @@ object NodeParams { globalFeatures = ByteVector.fromValidHex(config.getString("global-features")), localFeatures = ByteVector.fromValidHex(config.getString("local-features")), overrideFeatures = overrideFeatures, - dustLimitSatoshis = dustLimitSatoshis, + dustLimit = dustLimitSatoshis, onChainFeeConf = OnChainFeeConf( feeTargets = feeTargets, feeEstimator = feeEstimator, @@ -206,11 +205,11 @@ object NodeParams { maxAcceptedHtlcs = maxAcceptedHtlcs, expiryDeltaBlocks = expiryDeltaBlocks, fulfillSafetyBeforeTimeoutBlocks = fulfillSafetyBeforeTimeoutBlocks, - htlcMinimumMsat = config.getInt("htlc-minimum-msat"), + htlcMinimum = MilliSatoshi(config.getInt("htlc-minimum-msat")), toRemoteDelayBlocks = config.getInt("to-remote-delay-blocks"), maxToLocalDelayBlocks = config.getInt("max-to-local-delay-blocks"), minDepthBlocks = config.getInt("mindepth-blocks"), - feeBaseMsat = config.getInt("fee-base-msat"), + feeBase = MilliSatoshi(config.getInt("fee-base-msat")), feeProportionalMillionth = config.getInt("fee-proportional-millionths"), reserveToFundingRatio = config.getDouble("reserve-to-funding-ratio"), maxReserveToFundingRatio = config.getDouble("max-reserve-to-funding-ratio"), @@ -226,14 +225,14 @@ object NodeParams { channelFlags = config.getInt("channel-flags").toByte, watcherType = watcherType, paymentRequestExpiry = FiniteDuration(config.getDuration("payment-request-expiry").getSeconds, TimeUnit.SECONDS), - minFundingSatoshis = config.getLong("min-funding-satoshis"), + minFundingSatoshis = Satoshi(config.getLong("min-funding-satoshis")), routerConf = RouterConf( channelExcludeDuration = FiniteDuration(config.getDuration("router.channel-exclude-duration").getSeconds, TimeUnit.SECONDS), routerBroadcastInterval = FiniteDuration(config.getDuration("router.broadcast-interval").getSeconds, TimeUnit.SECONDS), randomizeRouteSelection = config.getBoolean("router.randomize-route-selection"), searchMaxRouteLength = config.getInt("router.path-finding.max-route-length"), searchMaxCltv = config.getInt("router.path-finding.max-cltv"), - searchMaxFeeBaseSat = config.getLong("router.path-finding.fee-threshold-sat"), + searchMaxFeeBase = Satoshi(config.getLong("router.path-finding.fee-threshold-sat")), searchMaxFeePct = config.getDouble("router.path-finding.max-fee-pct"), searchHeuristicsEnabled = config.getBoolean("router.path-finding.heuristics-enable"), searchRatioCltv = config.getDouble("router.path-finding.ratio-cltv"), diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/api/ExtraDirectives.scala b/eclair-core/src/main/scala/fr/acinq/eclair/api/ExtraDirectives.scala index 9d5e1414f0..2973b53ec6 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/api/ExtraDirectives.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/api/ExtraDirectives.scala @@ -22,10 +22,11 @@ import akka.http.scaladsl.model.StatusCodes._ import akka.http.scaladsl.server.{Directive1, Directives, MalformedFormFieldRejection, Route} import fr.acinq.bitcoin.ByteVector32 import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.eclair.ShortChannelId +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId} import fr.acinq.eclair.api.FormParamExtractors.{sha256HashUnmarshaller, shortChannelIdUnmarshaller} import fr.acinq.eclair.api.JsonSupport._ import fr.acinq.eclair.payment.PaymentRequest + import scala.concurrent.Future import scala.util.{Failure, Success} @@ -38,7 +39,7 @@ trait ExtraDirectives extends Directives { val paymentHashFormParam = "paymentHash".as[ByteVector32](sha256HashUnmarshaller) val fromFormParam = "from".as[Long] val toFormParam = "to".as[Long] - val amountMsatFormParam = "amountMsat".as[Long] + val amountMsatFormParam = "amountMsat".as[MilliSatoshi] val invoiceFormParam = "invoice".as[PaymentRequest] // custom directive to fail with HTTP 404 (and JSON response) if the element was not found diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/api/FormParamExtractors.scala b/eclair-core/src/main/scala/fr/acinq/eclair/api/FormParamExtractors.scala index 17425cf368..cbe045faa8 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/api/FormParamExtractors.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/api/FormParamExtractors.scala @@ -21,12 +21,13 @@ import java.util.UUID import JsonSupport._ import akka.http.scaladsl.unmarshalling.Unmarshaller import akka.util.Timeout -import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.eclair.ShortChannelId +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId} import fr.acinq.eclair.io.NodeURI import fr.acinq.eclair.payment.PaymentRequest import scodec.bits.ByteVector + import scala.concurrent.duration._ import scala.util.{Failure, Success, Try} @@ -75,4 +76,13 @@ object FormParamExtractors { } } + implicit val satoshiUnmarshaller: Unmarshaller[String, Satoshi] = Unmarshaller.strict { str => + Satoshi(str.toLong) + } + + implicit val millisatoshiUnmarshaller: Unmarshaller[String, MilliSatoshi] = Unmarshaller.strict { str => + MilliSatoshi(str.toLong) + } + + } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala b/eclair-core/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala index a1a586941f..203c6c7f35 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala @@ -23,7 +23,7 @@ import com.google.common.net.HostAndPort import de.heikoseeberger.akkahttpjson4s.Json4sSupport import de.heikoseeberger.akkahttpjson4s.Json4sSupport.ShouldWritePretty import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin.{ByteVector32, ByteVector64, MilliSatoshi, OutPoint, Transaction} +import fr.acinq.bitcoin.{ByteVector32, ByteVector64, OutPoint, Satoshi, Transaction} import fr.acinq.eclair.channel.{ChannelVersion, State} import fr.acinq.eclair.crypto.ShaChain import fr.acinq.eclair.db.OutgoingPaymentStatus @@ -32,7 +32,7 @@ import fr.acinq.eclair.router.RouteResponse import fr.acinq.eclair.transactions.Direction import fr.acinq.eclair.transactions.Transactions.{InputInfo, TransactionWithInputInfo} import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{ShortChannelId, UInt64} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, UInt64} import org.json4s.JsonAST._ import org.json4s.{CustomKeySerializer, CustomSerializer, TypeHints, jackson} import scodec.bits.ByteVector @@ -57,6 +57,10 @@ class UInt64Serializer extends CustomSerializer[UInt64](format => ({ null }, { case x: UInt64 => JInt(x.toBigInt) })) +class SatoshiSerializer extends CustomSerializer[Satoshi](format => ({ null }, { + case x: Satoshi => JInt(x.amount) +})) + class MilliSatoshiSerializer extends CustomSerializer[MilliSatoshi](format => ({ null }, { case x: MilliSatoshi => JInt(x.amount) })) @@ -187,6 +191,7 @@ object JsonSupport extends Json4sSupport { new ByteVector32Serializer + new ByteVector64Serializer + new UInt64Serializer + + new SatoshiSerializer + new MilliSatoshiSerializer + new ShortChannelIdSerializer + new StateSerializer + diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/api/Service.scala b/eclair-core/src/main/scala/fr/acinq/eclair/api/Service.scala index 3d781cadc5..7d50d75217 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/api/Service.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/api/Service.scala @@ -31,14 +31,14 @@ import akka.stream.scaladsl.{BroadcastHub, Flow, Keep, Source} import akka.stream.{ActorMaterializer, OverflowStrategy} import akka.util.Timeout import com.google.common.net.HostAndPort -import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.eclair.api.FormParamExtractors._ import fr.acinq.eclair.api.JsonSupport.CustomTypeHints import fr.acinq.eclair.io.NodeURI import fr.acinq.eclair.payment.PaymentLifecycle.PaymentFailed import fr.acinq.eclair.payment.{PaymentReceived, PaymentRequest, _} -import fr.acinq.eclair.{Eclair, ShortChannelId} +import fr.acinq.eclair.{Eclair, MilliSatoshi, ShortChannelId} import grizzled.slf4j.Logging import org.json4s.jackson.Serialization import scodec.bits.ByteVector @@ -156,14 +156,14 @@ trait Service extends ExtraDirectives with Logging { } } ~ path("open") { - formFields(nodeIdFormParam, "fundingSatoshis".as[Long], "pushMsat".as[Long].?, "fundingFeerateSatByte".as[Long].?, "channelFlags".as[Int].?, "openTimeoutSeconds".as[Timeout].?) { + formFields(nodeIdFormParam, "fundingSatoshis".as[Satoshi], "pushMsat".as[MilliSatoshi].?, "fundingFeerateSatByte".as[Long].?, "channelFlags".as[Int].?, "openTimeoutSeconds".as[Timeout].?) { (nodeId, fundingSatoshis, pushMsat, fundingFeerateSatByte, channelFlags, openTimeout_opt) => complete(eclairApi.open(nodeId, fundingSatoshis, pushMsat, fundingFeerateSatByte, channelFlags, openTimeout_opt)) } } ~ path("updaterelayfee") { withChannelIdentifier { channelIdentifier => - formFields("feeBaseMsat".as[Long], "feeProportionalMillionths".as[Long]) { (feeBase, feeProportional) => + formFields("feeBaseMsat".as[MilliSatoshi], "feeProportionalMillionths".as[Long]) { (feeBase, feeProportional) => complete(eclairApi.updateRelayFee(channelIdentifier, feeBase, feeProportional)) } } @@ -206,7 +206,7 @@ trait Service extends ExtraDirectives with Logging { } ~ path("findroute") { formFields(invoiceFormParam, amountMsatFormParam.?) { - case (invoice@PaymentRequest(_, Some(amount), _, nodeId, _, _), None) => complete(eclairApi.findRoute(nodeId, amount.toLong, invoice.routingInfo)) + case (invoice@PaymentRequest(_, Some(amount), _, nodeId, _, _), None) => complete(eclairApi.findRoute(nodeId, amount, invoice.routingInfo)) case (invoice, Some(overrideAmount)) => complete(eclairApi.findRoute(invoice.nodeId, overrideAmount, invoice.routingInfo)) case _ => reject(MalformedFormFieldRejection("invoice", "The invoice must have an amount or you need to specify one using 'amountMsat'")) } @@ -222,16 +222,16 @@ trait Service extends ExtraDirectives with Logging { } } ~ path("payinvoice") { - formFields(invoiceFormParam, amountMsatFormParam.?, "maxAttempts".as[Int].?, "feeThresholdSat".as[Long].?, "maxFeePct".as[Double].?) { + formFields(invoiceFormParam, amountMsatFormParam.?, "maxAttempts".as[Int].?, "feeThresholdSat".as[Satoshi].?, "maxFeePct".as[Double].?) { case (invoice@PaymentRequest(_, Some(amount), _, nodeId, _, _), None, maxAttempts, feeThresholdSat_opt, maxFeePct_opt) => - complete(eclairApi.send(nodeId, amount.toLong, invoice.paymentHash, invoice.routingInfo, invoice.minFinalCltvExpiry, maxAttempts, feeThresholdSat_opt, maxFeePct_opt)) + complete(eclairApi.send(nodeId, amount, invoice.paymentHash, invoice.routingInfo, invoice.minFinalCltvExpiry, maxAttempts, feeThresholdSat_opt, maxFeePct_opt)) case (invoice, Some(overrideAmount), maxAttempts, feeThresholdSat_opt, maxFeePct_opt) => complete(eclairApi.send(invoice.nodeId, overrideAmount, invoice.paymentHash, invoice.routingInfo, invoice.minFinalCltvExpiry, maxAttempts, feeThresholdSat_opt, maxFeePct_opt)) case _ => reject(MalformedFormFieldRejection("invoice", "The invoice must have an amount or you need to specify one using the field 'amountMsat'")) } } ~ path("sendtonode") { - formFields(amountMsatFormParam, paymentHashFormParam, nodeIdFormParam, "maxAttempts".as[Int].?, "feeThresholdSat".as[Long].?, "maxFeePct".as[Double].?) { (amountMsat, paymentHash, nodeId, maxAttempts_opt, feeThresholdSat_opt, maxFeePct_opt) => + formFields(amountMsatFormParam, paymentHashFormParam, nodeIdFormParam, "maxAttempts".as[Int].?, "feeThresholdSat".as[Satoshi].?, "maxFeePct".as[Double].?) { (amountMsat, paymentHash, nodeId, maxAttempts_opt, feeThresholdSat_opt, maxFeePct_opt) => complete(eclairApi.send(nodeId, amountMsat, paymentHash, maxAttempts_opt = maxAttempts_opt, feeThresholdSat_opt = feeThresholdSat_opt, maxFeePct_opt = maxFeePct_opt)) } } ~ diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala index 72585ee2fd..d095f595fb 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala @@ -20,7 +20,7 @@ import akka.actor.{ActorRef, FSM, OneForOneStrategy, Props, Status, SupervisorSt import akka.event.Logging.MDC import akka.pattern.pipe import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey, sha256} -import fr.acinq.bitcoin._ +import fr.acinq.bitcoin.{ByteVector32, OutPoint, Satoshi, Script, ScriptFlags, Transaction} import fr.acinq.eclair._ import fr.acinq.eclair.blockchain._ import fr.acinq.eclair.channel.Helpers.{Closing, Funding} @@ -48,11 +48,11 @@ object Channel { val ANNOUNCEMENTS_MINCONF = 6 // https://github.com/lightningnetwork/lightning-rfc/blob/master/02-peer-protocol.md#requirements - val MAX_FUNDING_SATOSHIS = 16777216L // = 2^24 + val MAX_FUNDING = Satoshi(16777216L) // = 2^24 val MAX_ACCEPTED_HTLCS = 483 // we don't want the counterparty to use a dust limit lower than that, because they wouldn't only hurt themselves we may need them to publish their commit tx in certain cases (backup/restore) - val MIN_DUSTLIMIT = 546 + val MIN_DUSTLIMIT = Satoshi(546) // we won't exchange more than this many signatures when negotiating the closing fee val MAX_NEGOTIATION_ITERATIONS = 20 @@ -152,10 +152,10 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId temporaryChannelId = temporaryChannelId, fundingSatoshis = fundingSatoshis, pushMsat = pushMsat, - dustLimitSatoshis = localParams.dustLimitSatoshis, + dustLimitSatoshis = localParams.dustLimit, maxHtlcValueInFlightMsat = localParams.maxHtlcValueInFlightMsat, - channelReserveSatoshis = localParams.channelReserveSatoshis, - htlcMinimumMsat = localParams.htlcMinimumMsat, + channelReserveSatoshis = localParams.channelReserve, + htlcMinimumMsat = localParams.htlcMinimum, feeratePerKw = initialFeeratePerKw, toSelfDelay = localParams.toSelfDelay, maxAcceptedHtlcs = localParams.maxAcceptedHtlcs, @@ -227,7 +227,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId // we rebuild a new channel_update with values from the configuration because they may have changed while eclair was down val candidateChannelUpdate = Announcements.makeChannelUpdate(nodeParams.chainHash, nodeParams.privateKey, remoteNodeId, normal.channelUpdate.shortChannelId, nodeParams.expiryDeltaBlocks, - normal.commitments.remoteParams.htlcMinimumMsat, normal.channelUpdate.feeBaseMsat, normal.channelUpdate.feeProportionalMillionths, normal.commitments.localCommit.spec.totalFunds, enable = Announcements.isEnabled(normal.channelUpdate.channelFlags)) + normal.commitments.remoteParams.htlcMinimum, normal.channelUpdate.feeBaseMsat, normal.channelUpdate.feeProportionalMillionths, normal.commitments.localCommit.spec.totalFunds, enable = Announcements.isEnabled(normal.channelUpdate.channelFlags)) val channelUpdate1 = if (Announcements.areSame(candidateChannelUpdate, normal.channelUpdate)) { // if there was no configuration change we keep the existing channel update normal.channelUpdate @@ -274,11 +274,11 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId // TODO: maybe also check uniqueness of temporary channel id val minimumDepth = nodeParams.minDepthBlocks val accept = AcceptChannel(temporaryChannelId = open.temporaryChannelId, - dustLimitSatoshis = localParams.dustLimitSatoshis, + dustLimitSatoshis = localParams.dustLimit, maxHtlcValueInFlightMsat = localParams.maxHtlcValueInFlightMsat, - channelReserveSatoshis = localParams.channelReserveSatoshis, + channelReserveSatoshis = localParams.channelReserve, minimumDepth = minimumDepth, - htlcMinimumMsat = localParams.htlcMinimumMsat, + htlcMinimumMsat = localParams.htlcMinimum, toSelfDelay = localParams.toSelfDelay, maxAcceptedHtlcs = localParams.maxAcceptedHtlcs, fundingPubkey = keyManager.fundingPublicKey(localParams.channelKeyPath).publicKey, @@ -289,10 +289,10 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId firstPerCommitmentPoint = keyManager.commitmentPoint(localParams.channelKeyPath, 0)) val remoteParams = RemoteParams( nodeId = remoteNodeId, - dustLimitSatoshis = open.dustLimitSatoshis, + dustLimit = open.dustLimitSatoshis, maxHtlcValueInFlightMsat = open.maxHtlcValueInFlightMsat, - channelReserveSatoshis = open.channelReserveSatoshis, // remote requires local to keep this much satoshis as direct payment - htlcMinimumMsat = open.htlcMinimumMsat, + channelReserve = open.channelReserveSatoshis, // remote requires local to keep this much satoshis as direct payment + htlcMinimum = open.htlcMinimumMsat, toSelfDelay = open.toSelfDelay, maxAcceptedHtlcs = open.maxAcceptedHtlcs, fundingPubKey = open.fundingPubkey, @@ -322,10 +322,10 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId // TODO: check equality of temporaryChannelId? or should be done upstream val remoteParams = RemoteParams( nodeId = remoteNodeId, - dustLimitSatoshis = accept.dustLimitSatoshis, + dustLimit = accept.dustLimitSatoshis, maxHtlcValueInFlightMsat = accept.maxHtlcValueInFlightMsat, - channelReserveSatoshis = accept.channelReserveSatoshis, // remote requires local to keep this much satoshis as direct payment - htlcMinimumMsat = accept.htlcMinimumMsat, + channelReserve = accept.channelReserveSatoshis, // remote requires local to keep this much satoshis as direct payment + htlcMinimum = accept.htlcMinimumMsat, toSelfDelay = accept.toSelfDelay, maxAcceptedHtlcs = accept.maxAcceptedHtlcs, fundingPubKey = accept.fundingPubkey, @@ -338,7 +338,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId log.debug(s"remote params: $remoteParams") val localFundingPubkey = keyManager.fundingPublicKey(localParams.channelKeyPath).publicKey val fundingPubkeyScript = Script.write(Script.pay2wsh(Scripts.multiSig2of2(localFundingPubkey, remoteParams.fundingPubKey))) - wallet.makeFundingTx(fundingPubkeyScript, Satoshi(fundingSatoshis), fundingTxFeeratePerKw).pipeTo(self) + wallet.makeFundingTx(fundingPubkeyScript, fundingSatoshis, fundingTxFeeratePerKw).pipeTo(self) goto(WAIT_FOR_FUNDING_INTERNAL) using DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId, localParams, remoteParams, fundingSatoshis, pushMsat, initialFeeratePerKw, accept.firstPerCommitmentPoint, open) } @@ -360,9 +360,9 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId }) when(WAIT_FOR_FUNDING_INTERNAL)(handleExceptions { - case Event(MakeFundingTxResponse(fundingTx, fundingTxOutputIndex, fundingTxFee), DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId, localParams, remoteParams, fundingSatoshis, pushMsat, initialFeeratePerKw, remoteFirstPerCommitmentPoint, open)) => + case Event(MakeFundingTxResponse(fundingTx, fundingTxOutputIndex, fundingTxFee), DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId, localParams, remoteParams, fundingAmount, pushMsat, initialFeeratePerKw, remoteFirstPerCommitmentPoint, open)) => // let's create the first commitment tx that spends the yet uncommitted funding tx - val (localSpec, localCommitTx, remoteSpec, remoteCommitTx) = Funding.makeFirstCommitTxs(keyManager, temporaryChannelId, localParams, remoteParams, fundingSatoshis, pushMsat, initialFeeratePerKw, fundingTx.hash, fundingTxOutputIndex, remoteFirstPerCommitmentPoint, nodeParams.onChainFeeConf.maxFeerateMismatch) + val (localSpec, localCommitTx, remoteSpec, remoteCommitTx) = Funding.makeFirstCommitTxs(keyManager, temporaryChannelId, localParams, remoteParams, fundingAmount, pushMsat, initialFeeratePerKw, fundingTx.hash, fundingTxOutputIndex, remoteFirstPerCommitmentPoint, nodeParams.onChainFeeConf.maxFeerateMismatch) require(fundingTx.txOut(fundingTxOutputIndex).publicKeyScript == localCommitTx.input.txOut.publicKeyScript, s"pubkey script mismatch!") val localSigOfRemoteTx = keyManager.sign(remoteCommitTx, keyManager.fundingPublicKey(localParams.channelKeyPath)) // signature of their initial commitment tx that pays remote pushMsat @@ -401,9 +401,9 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId }) when(WAIT_FOR_FUNDING_CREATED)(handleExceptions { - case Event(FundingCreated(_, fundingTxHash, fundingTxOutputIndex, remoteSig), d@DATA_WAIT_FOR_FUNDING_CREATED(temporaryChannelId, localParams, remoteParams, fundingSatoshis, pushMsat, initialFeeratePerKw, remoteFirstPerCommitmentPoint, channelFlags, _)) => + case Event(FundingCreated(_, fundingTxHash, fundingTxOutputIndex, remoteSig), d@DATA_WAIT_FOR_FUNDING_CREATED(temporaryChannelId, localParams, remoteParams, fundingAmount, pushMsat, initialFeeratePerKw, remoteFirstPerCommitmentPoint, channelFlags, _)) => // they fund the channel with their funding tx, so the money is theirs (but we are paid pushMsat) - val (localSpec, localCommitTx, remoteSpec, remoteCommitTx) = Funding.makeFirstCommitTxs(keyManager, temporaryChannelId, localParams, remoteParams, fundingSatoshis: Long, pushMsat, initialFeeratePerKw, fundingTxHash, fundingTxOutputIndex, remoteFirstPerCommitmentPoint, nodeParams.onChainFeeConf.maxFeerateMismatch) + val (localSpec, localCommitTx, remoteSpec, remoteCommitTx) = Funding.makeFirstCommitTxs(keyManager, temporaryChannelId, localParams, remoteParams, fundingAmount, pushMsat, initialFeeratePerKw, fundingTxHash, fundingTxOutputIndex, remoteFirstPerCommitmentPoint, nodeParams.onChainFeeConf.maxFeerateMismatch) // check remote signature validity val localSigOfLocalTx = keyManager.sign(localCommitTx, keyManager.fundingPublicKey(localParams.channelKeyPath)) @@ -561,7 +561,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId blockchain ! WatchConfirmed(self, commitments.commitInput.outPoint.txid, commitments.commitInput.txOut.publicKeyScript, ANNOUNCEMENTS_MINCONF, BITCOIN_FUNDING_DEEPLYBURIED) context.system.eventStream.publish(ShortChannelIdAssigned(self, commitments.channelId, shortChannelId)) // we create a channel_update early so that we can use it to send payments through this channel, but it won't be propagated to other nodes since the channel is not yet announced - val initialChannelUpdate = Announcements.makeChannelUpdate(nodeParams.chainHash, nodeParams.privateKey, remoteNodeId, shortChannelId, nodeParams.expiryDeltaBlocks, d.commitments.remoteParams.htlcMinimumMsat, nodeParams.feeBaseMsat, nodeParams.feeProportionalMillionth, commitments.localCommit.spec.totalFunds, enable = Helpers.aboveReserve(d.commitments)) + val initialChannelUpdate = Announcements.makeChannelUpdate(nodeParams.chainHash, nodeParams.privateKey, remoteNodeId, shortChannelId, nodeParams.expiryDeltaBlocks, d.commitments.remoteParams.htlcMinimum, nodeParams.feeBase, nodeParams.feeProportionalMillionth, commitments.localCommit.spec.totalFunds, enable = Helpers.aboveReserve(d.commitments)) // we need to periodically re-send channel updates, otherwise channel will be considered stale and get pruned by network context.system.scheduler.schedule(initialDelay = REFRESH_CHANNEL_UPDATE_INTERVAL, interval = REFRESH_CHANNEL_UPDATE_INTERVAL, receiver = self, message = BroadcastChannelUpdate(PeriodicRefresh)) goto(NORMAL) using DATA_NORMAL(commitments.copy(remoteNextCommitInfo = Right(nextPerCommitmentPoint)), shortChannelId, buried = false, None, initialChannelUpdate, None, None) storing() @@ -704,7 +704,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId val nextCommitNumber = nextRemoteCommit.index // we persist htlc data in order to be able to claim htlc outputs in case a revoked tx is published by our // counterparty, so only htlcs above remote's dust_limit matter - val trimmedHtlcs = Transactions.trimOfferedHtlcs(Satoshi(d.commitments.remoteParams.dustLimitSatoshis), nextRemoteCommit.spec) ++ Transactions.trimReceivedHtlcs(Satoshi(d.commitments.remoteParams.dustLimitSatoshis), nextRemoteCommit.spec) + val trimmedHtlcs = Transactions.trimOfferedHtlcs(d.commitments.remoteParams.dustLimit, nextRemoteCommit.spec) ++ Transactions.trimReceivedHtlcs(d.commitments.remoteParams.dustLimit, nextRemoteCommit.spec) trimmedHtlcs collect { case DirectedHtlc(_, u) => log.info(s"adding paymentHash=${u.paymentHash} cltvExpiry=${u.cltvExpiry} to htlcs db for commitNumber=$nextCommitNumber") @@ -716,9 +716,9 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId self ! BroadcastChannelUpdate(AboveReserve) } context.system.eventStream.publish(ChannelSignatureSent(self, commitments1)) - if (nextRemoteCommit.spec.toRemoteMsat != d.commitments.remoteCommit.spec.toRemoteMsat) { + if (nextRemoteCommit.spec.toRemote != d.commitments.remoteCommit.spec.toRemote) { // we send this event only when our balance changes (note that remoteCommit.toRemote == toLocal) - context.system.eventStream.publish(AvailableBalanceChanged(self, d.channelId, d.shortChannelId, nextRemoteCommit.spec.toRemoteMsat, commitments1)) + context.system.eventStream.publish(AvailableBalanceChanged(self, d.channelId, d.shortChannelId, nextRemoteCommit.spec.toRemote, commitments1)) } // we expect a quick response from our peer setTimer(RevocationTimeout.toString, RevocationTimeout(commitments1.remoteCommit.index, peer = context.parent), timeout = nodeParams.revocationTimeout, repeat = false) @@ -1159,21 +1159,21 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId when(NEGOTIATING)(handleExceptions { case Event(c@ClosingSigned(_, remoteClosingFee, remoteSig), d: DATA_NEGOTIATING) => log.info(s"received closingFeeSatoshis=$remoteClosingFee") - Closing.checkClosingSignature(keyManager, d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, Satoshi(remoteClosingFee), remoteSig) match { + Closing.checkClosingSignature(keyManager, d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, remoteClosingFee, remoteSig) match { case Success(signedClosingTx) if d.closingTxProposed.last.lastOption.map(_.localClosingSigned.feeSatoshis).contains(remoteClosingFee) || d.closingTxProposed.flatten.size >= MAX_NEGOTIATION_ITERATIONS => // we close when we converge or when there were too many iterations handleMutualClose(signedClosingTx, Left(d.copy(bestUnpublishedClosingTx_opt = Some(signedClosingTx)))) case Success(signedClosingTx) => // if we are fundee and we were waiting for them to send their first closing_signed, we don't have a lastLocalClosingFee, so we compute a firstClosingFee - val lastLocalClosingFee = d.closingTxProposed.last.lastOption.map(_.localClosingSigned.feeSatoshis).map(Satoshi) + val lastLocalClosingFee = d.closingTxProposed.last.lastOption.map(_.localClosingSigned.feeSatoshis) val nextClosingFee = Closing.nextClosingFee( localClosingFee = lastLocalClosingFee.getOrElse(Closing.firstClosingFee(d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, nodeParams.onChainFeeConf.feeEstimator, nodeParams.onChainFeeConf.feeTargets)), - remoteClosingFee = Satoshi(remoteClosingFee)) + remoteClosingFee = remoteClosingFee) val (closingTx, closingSigned) = Closing.makeClosingTx(keyManager, d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, nextClosingFee) if (lastLocalClosingFee.contains(nextClosingFee)) { // next computed fee is the same than the one we previously sent (probably because of rounding), let's close now handleMutualClose(signedClosingTx, Left(d.copy(bestUnpublishedClosingTx_opt = Some(signedClosingTx)))) - } else if (nextClosingFee == Satoshi(remoteClosingFee)) { + } else if (nextClosingFee == remoteClosingFee) { // we have converged! val closingTxProposed1 = d.closingTxProposed match { case previousNegotiations :+ currentNegotiation => previousNegotiations :+ (currentNegotiation :+ ClosingTxProposed(closingTx.tx, closingSigned)) @@ -1310,9 +1310,9 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId } // we may need to fail some htlcs in case a commitment tx was published and they have reached the timeout threshold val timedoutHtlcs = - Closing.timedoutHtlcs(d.commitments.localCommit, Satoshi(d.commitments.localParams.dustLimitSatoshis), tx) ++ - Closing.timedoutHtlcs(d.commitments.remoteCommit, Satoshi(d.commitments.remoteParams.dustLimitSatoshis), tx) ++ - d.commitments.remoteNextCommitInfo.left.toSeq.flatMap(r => Closing.timedoutHtlcs(r.nextRemoteCommit, Satoshi(d.commitments.remoteParams.dustLimitSatoshis), tx)) + Closing.timedoutHtlcs(d.commitments.localCommit, d.commitments.localParams.dustLimit, tx) ++ + Closing.timedoutHtlcs(d.commitments.remoteCommit, d.commitments.remoteParams.dustLimit, tx) ++ + d.commitments.remoteNextCommitInfo.left.toSeq.flatMap(r => Closing.timedoutHtlcs(r.nextRemoteCommit, d.commitments.remoteParams.dustLimit, tx)) timedoutHtlcs.foreach { add => d.commitments.originChannels.get(add.id) match { case Some(origin) => @@ -1339,7 +1339,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId Closing .onchainOutgoingHtlcs(d.commitments.localCommit, d.commitments.remoteCommit, d.commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit), tx) .map(add => (add, d.commitments.originChannels.get(add.id).collect { case Local(id, _) => id })) // we resolve the payment id if this was a local payment - .collect { case (add, Some(id)) => context.system.eventStream.publish(PaymentSettlingOnChain(id, amount = MilliSatoshi(add.amountMsat), add.paymentHash)) } + .collect { case (add, Some(id)) => context.system.eventStream.publish(PaymentSettlingOnChain(id, amount = add.amountMsat, add.paymentHash)) } // we update the channel data val d1 = d.copy(localCommitPublished = localCommitPublished1, remoteCommitPublished = remoteCommitPublished1, nextRemoteCommitPublished = nextRemoteCommitPublished1, futureRemoteCommitPublished = futureRemoteCommitPublished1, revokedCommitPublished = revokedCommitPublished1) // and we also send events related to fee @@ -2207,7 +2207,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId def origin(c: CMD_ADD_HTLC): Origin = c.upstream match { case Left(id) => Local(id, Some(sender)) // we were the origin of the payment - case Right(u) => Relayed(u.channelId, u.id, u.amountMsat, c.amountMsat) // this is a relayed payment + case Right(u) => Relayed(u.channelId, u.id, u.amountMsat, c.amount) // this is a relayed payment } def feePaid(fee: Satoshi, tx: Transaction, desc: String, channelId: ByteVector32): Unit = { diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelEvents.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelEvents.scala index e9104a7db4..e80ff51eca 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelEvents.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelEvents.scala @@ -19,7 +19,8 @@ package fr.acinq.eclair.channel import akka.actor.ActorRef import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.bitcoin.{ByteVector32, Satoshi, Transaction} -import fr.acinq.eclair.ShortChannelId +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId} +import fr.acinq.eclair.api.MilliSatoshiSerializer import fr.acinq.eclair.channel.Channel.ChannelError import fr.acinq.eclair.channel.Helpers.Closing.ClosingType import fr.acinq.eclair.wire.{ChannelAnnouncement, ChannelUpdate} @@ -53,7 +54,7 @@ case class ChannelErrorOccured(channel: ActorRef, channelId: ByteVector32, remot case class NetworkFeePaid(channel: ActorRef, remoteNodeId: PublicKey, channelId: ByteVector32, tx: Transaction, fee: Satoshi, txType: String) extends ChannelEvent // NB: this event is only sent when the channel is available -case class AvailableBalanceChanged(channel: ActorRef, channelId: ByteVector32, shortChannelId: ShortChannelId, localBalanceMsat: Long, commitments: Commitments) extends ChannelEvent +case class AvailableBalanceChanged(channel: ActorRef, channelId: ByteVector32, shortChannelId: ShortChannelId, localBalance: MilliSatoshi, commitments: Commitments) extends ChannelEvent case class ChannelPersisted(channel: ActorRef, remoteNodeId: PublicKey, channelId: ByteVector32, data: Data) extends ChannelEvent diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelExceptions.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelExceptions.scala index 3546f22af6..56fc890eb5 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelExceptions.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelExceptions.scala @@ -17,8 +17,8 @@ package fr.acinq.eclair.channel import fr.acinq.bitcoin.Crypto.PrivateKey -import fr.acinq.bitcoin.{ByteVector32, Transaction} -import fr.acinq.eclair.UInt64 +import fr.acinq.bitcoin.{ByteVector32, Satoshi, Transaction} +import fr.acinq.eclair.{MilliSatoshi, UInt64} import fr.acinq.eclair.payment.Origin import fr.acinq.eclair.wire.{ChannelUpdate, UpdateAddHtlc} @@ -31,16 +31,16 @@ class ChannelException(val channelId: ByteVector32, message: String) extends Run // @formatter:off case class DebugTriggeredException (override val channelId: ByteVector32) extends ChannelException(channelId, "debug-mode triggered failure") case class InvalidChainHash (override val channelId: ByteVector32, local: ByteVector32, remote: ByteVector32) extends ChannelException(channelId, s"invalid chainHash (local=$local remote=$remote)") -case class InvalidFundingAmount (override val channelId: ByteVector32, fundingSatoshis: Long, min: Long, max: Long) extends ChannelException(channelId, s"invalid funding_satoshis=$fundingSatoshis (min=$min max=$max)") -case class InvalidPushAmount (override val channelId: ByteVector32, pushMsat: Long, max: Long) extends ChannelException(channelId, s"invalid pushMsat=$pushMsat (max=$max)") +case class InvalidFundingAmount (override val channelId: ByteVector32, fundingAmount: Satoshi, min: Satoshi, max: Satoshi) extends ChannelException(channelId, s"invalid funding_satoshis=$fundingAmount (min=$min max=$max)") +case class InvalidPushAmount (override val channelId: ByteVector32, pushAmount: MilliSatoshi, max: MilliSatoshi) extends ChannelException(channelId, s"invalid pushAmount=$pushAmount (max=$max)") case class InvalidMaxAcceptedHtlcs (override val channelId: ByteVector32, maxAcceptedHtlcs: Int, max: Int) extends ChannelException(channelId, s"invalid max_accepted_htlcs=$maxAcceptedHtlcs (max=$max)") -case class DustLimitTooSmall (override val channelId: ByteVector32, dustLimitSatoshis: Long, min: Long) extends ChannelException(channelId, s"dustLimitSatoshis=$dustLimitSatoshis is too small (min=$min)") -case class DustLimitTooLarge (override val channelId: ByteVector32, dustLimitSatoshis: Long, max: Long) extends ChannelException(channelId, s"dustLimitSatoshis=$dustLimitSatoshis is too large (max=$max)") -case class DustLimitAboveOurChannelReserve (override val channelId: ByteVector32, dustLimitSatoshis: Long, channelReserveSatoshis: Long) extends ChannelException(channelId, s"dustLimitSatoshis dustLimitSatoshis=$dustLimitSatoshis is above our channelReserveSatoshis=$channelReserveSatoshis") +case class DustLimitTooSmall (override val channelId: ByteVector32, dustLimit: Satoshi, min: Satoshi) extends ChannelException(channelId, s"dustLimit=$dustLimit is too small (min=$min)") +case class DustLimitTooLarge (override val channelId: ByteVector32, dustLimit: Satoshi, max: Satoshi) extends ChannelException(channelId, s"dustLimit=$dustLimit is too large (max=$max)") +case class DustLimitAboveOurChannelReserve (override val channelId: ByteVector32, dustLimit: Satoshi, channelReserve: Satoshi) extends ChannelException(channelId, s"dustLimit=$dustLimit is above our channelReserve=$channelReserve") case class ToSelfDelayTooHigh (override val channelId: ByteVector32, toSelfDelay: Int, max: Int) extends ChannelException(channelId, s"unreasonable to_self_delay=$toSelfDelay (max=$max)") -case class ChannelReserveTooHigh (override val channelId: ByteVector32, channelReserveSatoshis: Long, reserveToFundingRatio: Double, maxReserveToFundingRatio: Double) extends ChannelException(channelId, s"channelReserveSatoshis too high: reserve=$channelReserveSatoshis fundingRatio=$reserveToFundingRatio maxFundingRatio=$maxReserveToFundingRatio") -case class ChannelReserveBelowOurDustLimit (override val channelId: ByteVector32, channelReserveSatoshis: Long, dustLimitSatoshis: Long) extends ChannelException(channelId, s"their channelReserveSatoshis=$channelReserveSatoshis is below our dustLimitSatoshis=$dustLimitSatoshis") -case class ChannelReserveNotMet (override val channelId: ByteVector32, toLocalMsat: Long, toRemoteMsat: Long, reserveSatoshis: Long) extends ChannelException(channelId, s"channel reserve is not met toLocalMsat=$toLocalMsat toRemoteMsat=$toRemoteMsat reserveSat=$reserveSatoshis") +case class ChannelReserveTooHigh (override val channelId: ByteVector32, channelReserve: Satoshi, reserveToFundingRatio: Double, maxReserveToFundingRatio: Double) extends ChannelException(channelId, s"channelReserve too high: reserve=$channelReserve fundingRatio=$reserveToFundingRatio maxFundingRatio=$maxReserveToFundingRatio") +case class ChannelReserveBelowOurDustLimit (override val channelId: ByteVector32, channelReserve: Satoshi, dustLimit: Satoshi) extends ChannelException(channelId, s"their channelReserve=$channelReserve is below our dustLimit=$dustLimit") +case class ChannelReserveNotMet (override val channelId: ByteVector32, toLocal: MilliSatoshi, toRemote: MilliSatoshi, reserve: Satoshi) extends ChannelException(channelId, s"channel reserve is not met toLocal=$toLocal toRemote=$toRemote reserve=$reserve") case class ChannelFundingError (override val channelId: ByteVector32) extends ChannelException(channelId, "channel funding error") case class NoMoreHtlcsClosingInProgress (override val channelId: ByteVector32) extends ChannelException(channelId, "cannot send new htlcs, closing in progress") case class ClosingAlreadyInProgress (override val channelId: ByteVector32) extends ChannelException(channelId, "closing already in progress") @@ -57,21 +57,21 @@ case class FeerateTooDifferent (override val channelId: ByteVect case class InvalidCommitmentSignature (override val channelId: ByteVector32, tx: Transaction) extends ChannelException(channelId, s"invalid commitment signature: tx=$tx") case class InvalidHtlcSignature (override val channelId: ByteVector32, tx: Transaction) extends ChannelException(channelId, s"invalid htlc signature: tx=$tx") case class InvalidCloseSignature (override val channelId: ByteVector32, tx: Transaction) extends ChannelException(channelId, s"invalid close signature: tx=$tx") -case class InvalidCloseFee (override val channelId: ByteVector32, feeSatoshi: Long) extends ChannelException(channelId, s"invalid close fee: fee_satoshis=$feeSatoshi") +case class InvalidCloseFee (override val channelId: ByteVector32, fee: Satoshi) extends ChannelException(channelId, s"invalid close fee: fee_satoshis=$fee") case class HtlcSigCountMismatch (override val channelId: ByteVector32, expected: Int, actual: Int) extends ChannelException(channelId, s"htlc sig count mismatch: expected=$expected actual: $actual") case class ForcedLocalCommit (override val channelId: ByteVector32) extends ChannelException(channelId, s"forced local commit") case class UnexpectedHtlcId (override val channelId: ByteVector32, expected: Long, actual: Long) extends ChannelException(channelId, s"unexpected htlc id: expected=$expected actual=$actual") case class ExpiryTooSmall (override val channelId: ByteVector32, minimum: Long, actual: Long, blockCount: Long) extends ChannelException(channelId, s"expiry too small: minimum=$minimum actual=$actual blockCount=$blockCount") case class ExpiryTooBig (override val channelId: ByteVector32, maximum: Long, actual: Long, blockCount: Long) extends ChannelException(channelId, s"expiry too big: maximum=$maximum actual=$actual blockCount=$blockCount") -case class HtlcValueTooSmall (override val channelId: ByteVector32, minimum: Long, actual: Long) extends ChannelException(channelId, s"htlc value too small: minimum=$minimum actual=$actual") +case class HtlcValueTooSmall (override val channelId: ByteVector32, minimum: MilliSatoshi, actual: MilliSatoshi) extends ChannelException(channelId, s"htlc value too small: minimum=$minimum actual=$actual") case class HtlcValueTooHighInFlight (override val channelId: ByteVector32, maximum: UInt64, actual: UInt64) extends ChannelException(channelId, s"in-flight htlcs hold too much value: maximum=$maximum actual=$actual") case class TooManyAcceptedHtlcs (override val channelId: ByteVector32, maximum: Long) extends ChannelException(channelId, s"too many accepted htlcs: maximum=$maximum") -case class InsufficientFunds (override val channelId: ByteVector32, amountMsat: Long, missingSatoshis: Long, reserveSatoshis: Long, feesSatoshis: Long) extends ChannelException(channelId, s"insufficient funds: missingSatoshis=$missingSatoshis reserveSatoshis=$reserveSatoshis fees=$feesSatoshis") +case class InsufficientFunds (override val channelId: ByteVector32, amount: MilliSatoshi, missing: Satoshi, reserve: Satoshi, fees: Satoshi) extends ChannelException(channelId, s"insufficient funds: missing=$missing reserve=$reserve fees=$fees") case class InvalidHtlcPreimage (override val channelId: ByteVector32, id: Long) extends ChannelException(channelId, s"invalid htlc preimage for htlc id=$id") case class UnknownHtlcId (override val channelId: ByteVector32, id: Long) extends ChannelException(channelId, s"unknown htlc id=$id") case class CannotExtractSharedSecret (override val channelId: ByteVector32, htlc: UpdateAddHtlc) extends ChannelException(channelId, s"can't extract shared secret: paymentHash=${htlc.paymentHash} onion=${htlc.onionRoutingPacket}") case class FundeeCannotSendUpdateFee (override val channelId: ByteVector32) extends ChannelException(channelId, s"only the funder should send update_fee messages") -case class CannotAffordFees (override val channelId: ByteVector32, missingSatoshis: Long, reserveSatoshis: Long, feesSatoshis: Long) extends ChannelException(channelId, s"can't pay the fee: missingSatoshis=$missingSatoshis reserveSatoshis=$reserveSatoshis feesSatoshis=$feesSatoshis") +case class CannotAffordFees (override val channelId: ByteVector32, missing: Satoshi, reserve: Satoshi, fees: Satoshi) extends ChannelException(channelId, s"can't pay the fee: missing=$missing reserve=$reserve fees=$fees") case class CannotSignWithoutChanges (override val channelId: ByteVector32) extends ChannelException(channelId, "cannot sign when there are no changes") case class CannotSignBeforeRevocation (override val channelId: ByteVector32) extends ChannelException(channelId, "cannot sign until next revocation hash is received") case class UnexpectedRevocation (override val channelId: ByteVector32) extends ChannelException(channelId, "received unexpected RevokeAndAck message") diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelTypes.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelTypes.scala index 48e6d6b57f..151cf582d5 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelTypes.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelTypes.scala @@ -21,10 +21,11 @@ import java.util.UUID import akka.actor.ActorRef import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.bitcoin.{ByteVector32, DeterministicWallet, OutPoint, Satoshi, Transaction} +import fr.acinq.eclair.api.MilliSatoshiSerializer import fr.acinq.eclair.transactions.CommitmentSpec import fr.acinq.eclair.transactions.Transactions.CommitTx import fr.acinq.eclair.wire.{AcceptChannel, ChannelAnnouncement, ChannelReestablish, ChannelUpdate, ClosingSigned, FailureMessage, FundingCreated, FundingLocked, FundingSigned, Init, OnionRoutingPacket, OpenChannel, Shutdown, UpdateAddHtlc} -import fr.acinq.eclair.{ShortChannelId, UInt64} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, UInt64} import scodec.bits.{BitVector, ByteVector} @@ -75,7 +76,7 @@ case object ERR_INFORMATION_LEAK extends State 8888888888 Y8P 8888888888 888 Y888 888 "Y8888P" */ -case class INPUT_INIT_FUNDER(temporaryChannelId: ByteVector32, fundingSatoshis: Long, pushMsat: Long, initialFeeratePerKw: Long, fundingTxFeeratePerKw: Long, localParams: LocalParams, remote: ActorRef, remoteInit: Init, channelFlags: Byte) +case class INPUT_INIT_FUNDER(temporaryChannelId: ByteVector32, fundingAmount: Satoshi, pushAmount: MilliSatoshi, initialFeeratePerKw: Long, fundingTxFeeratePerKw: Long, localParams: LocalParams, remote: ActorRef, remoteInit: Init, channelFlags: Byte) case class INPUT_INIT_FUNDEE(temporaryChannelId: ByteVector32, localParams: LocalParams, remote: ActorRef, remoteInit: Init) case object INPUT_CLOSE_COMPLETE_TIMEOUT // when requesting a mutual close, we wait for as much as this timeout, then unilateral close case object INPUT_DISCONNECTED @@ -106,14 +107,14 @@ case class BITCOIN_PARENT_TX_CONFIRMED(childTx: Transaction) extends BitcoinEven */ sealed trait Command -final case class CMD_ADD_HTLC(amountMsat: Long, paymentHash: ByteVector32, cltvExpiry: Long, onion: OnionRoutingPacket, upstream: Either[UUID, UpdateAddHtlc], commit: Boolean = false, previousFailures: Seq[AddHtlcFailed] = Seq.empty) extends Command +final case class CMD_ADD_HTLC(amount: MilliSatoshi, paymentHash: ByteVector32, cltvExpiry: Long, onion: OnionRoutingPacket, upstream: Either[UUID, UpdateAddHtlc], commit: Boolean = false, previousFailures: Seq[AddHtlcFailed] = Seq.empty) extends Command final case class CMD_FULFILL_HTLC(id: Long, r: ByteVector32, commit: Boolean = false) extends Command final case class CMD_FAIL_HTLC(id: Long, reason: Either[ByteVector, FailureMessage], commit: Boolean = false) extends Command final case class CMD_FAIL_MALFORMED_HTLC(id: Long, onionHash: ByteVector32, failureCode: Int, commit: Boolean = false) extends Command final case class CMD_UPDATE_FEE(feeratePerKw: Long, commit: Boolean = false) extends Command final case object CMD_SIGN extends Command final case class CMD_CLOSE(scriptPubKey: Option[ByteVector]) extends Command -final case class CMD_UPDATE_RELAY_FEE(feeBaseMsat: Long, feeProportionalMillionths: Long) extends Command +final case class CMD_UPDATE_RELAY_FEE(feeBase: MilliSatoshi, feeProportionalMillionths: Long) extends Command final case object CMD_FORCECLOSE extends Command final case object CMD_GETSTATE extends Command final case object CMD_GETSTATEDATA extends Command @@ -148,8 +149,8 @@ case class RevokedCommitPublished(commitTx: Transaction, claimMainOutputTx: Opti final case class DATA_WAIT_FOR_OPEN_CHANNEL(initFundee: INPUT_INIT_FUNDEE) extends Data final case class DATA_WAIT_FOR_ACCEPT_CHANNEL(initFunder: INPUT_INIT_FUNDER, lastSent: OpenChannel) extends Data -final case class DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId: ByteVector32, localParams: LocalParams, remoteParams: RemoteParams, fundingSatoshis: Long, pushMsat: Long, initialFeeratePerKw: Long, remoteFirstPerCommitmentPoint: PublicKey, lastSent: OpenChannel) extends Data -final case class DATA_WAIT_FOR_FUNDING_CREATED(temporaryChannelId: ByteVector32, localParams: LocalParams, remoteParams: RemoteParams, fundingSatoshis: Long, pushMsat: Long, initialFeeratePerKw: Long, remoteFirstPerCommitmentPoint: PublicKey, channelFlags: Byte, lastSent: AcceptChannel) extends Data +final case class DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId: ByteVector32, localParams: LocalParams, remoteParams: RemoteParams, fundingAmount: Satoshi, pushAmount: MilliSatoshi, initialFeeratePerKw: Long, remoteFirstPerCommitmentPoint: PublicKey, lastSent: OpenChannel) extends Data +final case class DATA_WAIT_FOR_FUNDING_CREATED(temporaryChannelId: ByteVector32, localParams: LocalParams, remoteParams: RemoteParams, fundingAmount: Satoshi, pushAmount: MilliSatoshi, initialFeeratePerKw: Long, remoteFirstPerCommitmentPoint: PublicKey, channelFlags: Byte, lastSent: AcceptChannel) extends Data final case class DATA_WAIT_FOR_FUNDING_SIGNED(channelId: ByteVector32, localParams: LocalParams, remoteParams: RemoteParams, fundingTx: Transaction, fundingTxFee: Satoshi, localSpec: CommitmentSpec, localCommitTx: CommitTx, remoteCommit: RemoteCommit, channelFlags: Byte, lastSent: FundingCreated) extends Data final case class DATA_WAIT_FOR_FUNDING_CONFIRMED(commitments: Commitments, fundingTx: Option[Transaction], @@ -191,10 +192,10 @@ final case class DATA_WAIT_FOR_REMOTE_PUBLISH_FUTURE_COMMITMENT(commitments: Com final case class LocalParams(nodeId: PublicKey, channelKeyPath: DeterministicWallet.KeyPath, - dustLimitSatoshis: Long, + dustLimit: Satoshi, maxHtlcValueInFlightMsat: UInt64, - channelReserveSatoshis: Long, - htlcMinimumMsat: Long, + channelReserve: Satoshi, + htlcMinimum: MilliSatoshi, toSelfDelay: Int, maxAcceptedHtlcs: Int, isFunder: Boolean, @@ -203,10 +204,10 @@ final case class LocalParams(nodeId: PublicKey, localFeatures: ByteVector) final case class RemoteParams(nodeId: PublicKey, - dustLimitSatoshis: Long, + dustLimit: Satoshi, maxHtlcValueInFlightMsat: UInt64, - channelReserveSatoshis: Long, - htlcMinimumMsat: Long, + channelReserve: Satoshi, + htlcMinimum: MilliSatoshi, toSelfDelay: Int, maxAcceptedHtlcs: Int, fundingPubKey: PublicKey, diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala index 7c1bace272..21533f0dba 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala @@ -19,13 +19,15 @@ package fr.acinq.eclair.channel import akka.event.LoggingAdapter import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey, sha256} import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Crypto, Satoshi} +import fr.acinq.eclair +import fr.acinq.eclair._ import fr.acinq.eclair.blockchain.fee.{FeeEstimator, FeeTargets} import fr.acinq.eclair.crypto.{Generators, KeyManager, ShaChain, Sphinx} 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, UInt64} +import fr.acinq.eclair.{Globals, MilliSatoshi, UInt64} // @formatter:off case class LocalChanges(proposed: List[UpdateMessage], signed: List[UpdateMessage], acked: List[UpdateMessage]) { @@ -84,16 +86,16 @@ case class Commitments(channelVersion: ChannelVersion, val announceChannel: Boolean = (channelFlags & 0x01) != 0 - lazy val availableBalanceForSendMsat: Long = { + lazy val availableBalanceForSend: MilliSatoshi = { val reduced = CommitmentSpec.reduce(remoteCommit.spec, remoteChanges.acked, localChanges.proposed) - val feesMsat = if (localParams.isFunder) Transactions.commitTxFee(Satoshi(remoteParams.dustLimitSatoshis), reduced).amount * 1000 else 0 - math.max(reduced.toRemoteMsat - remoteParams.channelReserveSatoshis * 1000 - feesMsat, 0) + val feesMsat = if (localParams.isFunder) commitTxFee(remoteParams.dustLimit, reduced).toMilliSatoshi else MilliSatoshi(0) + maxOf(reduced.toRemote - remoteParams.channelReserve.toMilliSatoshi - feesMsat, MilliSatoshi(0)) } - lazy val availableBalanceForReceiveMsat: Long = { + lazy val availableBalanceForReceive: MilliSatoshi = { val reduced = CommitmentSpec.reduce(localCommit.spec, localChanges.acked, remoteChanges.proposed) - val feesMsat = if (localParams.isFunder) 0 else Transactions.commitTxFee(Satoshi(localParams.dustLimitSatoshis), reduced).amount * 1000 - math.max(reduced.toRemoteMsat - localParams.channelReserveSatoshis * 1000 - feesMsat, 0) + val feesMsat = if (localParams.isFunder) MilliSatoshi(0) else commitTxFee(localParams.dustLimit, reduced).toMilliSatoshi + maxOf(reduced.toRemote - localParams.channelReserve.toMilliSatoshi - feesMsat, MilliSatoshi(0)) } } @@ -132,12 +134,12 @@ object Commitments { return Left(ExpiryTooBig(commitments.channelId, maximum = maxExpiry, actual = cmd.cltvExpiry, blockCount = blockCount)) } - if (cmd.amountMsat < commitments.remoteParams.htlcMinimumMsat) { - return Left(HtlcValueTooSmall(commitments.channelId, minimum = commitments.remoteParams.htlcMinimumMsat, actual = cmd.amountMsat)) + if (cmd.amount < commitments.remoteParams.htlcMinimum) { + return Left(HtlcValueTooSmall(commitments.channelId, minimum = commitments.remoteParams.htlcMinimum, actual = cmd.amount)) } // let's compute the current commitment *as seen by them* with this change taken into account - val add = UpdateAddHtlc(commitments.channelId, commitments.localNextHtlcId, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add = UpdateAddHtlc(commitments.channelId, commitments.localNextHtlcId, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) // we increment the local htlc index and add an entry to the origins map val commitments1 = addLocalProposal(commitments, add).copy(localNextHtlcId = commitments.localNextHtlcId + 1, originChannels = commitments.originChannels + (add.id -> origin)) // we need to base the next current commitment on the last sig we sent, even if we didn't yet receive their revocation @@ -146,7 +148,7 @@ object Commitments { // the HTLC we are about to create is outgoing, but from their point of view it is incoming val outgoingHtlcs = reduced.htlcs.filter(_.direction == IN) - val htlcValueInFlight = UInt64(outgoingHtlcs.map(_.add.amountMsat).sum) + val htlcValueInFlight = UInt64(outgoingHtlcs.map(_.add.amountMsat).sum.toLong) if (htlcValueInFlight > commitments1.remoteParams.maxHtlcValueInFlightMsat) { // TODO: this should be a specific UPDATE error return Left(HtlcValueTooHighInFlight(commitments.channelId, maximum = commitments1.remoteParams.maxHtlcValueInFlightMsat, actual = htlcValueInFlight)) @@ -158,10 +160,10 @@ object Commitments { // a node cannot spend pending incoming htlcs, and need to keep funds above the reserve required by the counterparty, after paying the fee // we look from remote's point of view, so if local is funder remote doesn't pay the fees - val fees = if (commitments1.localParams.isFunder) Transactions.commitTxFee(Satoshi(commitments1.remoteParams.dustLimitSatoshis), reduced).amount else 0 - val missing = reduced.toRemoteMsat / 1000 - commitments1.remoteParams.channelReserveSatoshis - fees - if (missing < 0) { - return Left(InsufficientFunds(commitments.channelId, amountMsat = cmd.amountMsat, missingSatoshis = -1 * missing, reserveSatoshis = commitments1.remoteParams.channelReserveSatoshis, feesSatoshis = fees)) + val fees = if (commitments1.localParams.isFunder) commitTxFee(commitments1.remoteParams.dustLimit, reduced) else Satoshi(0) + val missing = reduced.toRemote.truncateToSatoshi - commitments1.remoteParams.channelReserve - fees + if (missing < Satoshi(0)) { + return Left(InsufficientFunds(commitments.channelId, amount = cmd.amount, missing = -missing, reserve = commitments1.remoteParams.channelReserve, fees = fees)) } Right(commitments1, add) @@ -172,8 +174,8 @@ object Commitments { throw UnexpectedHtlcId(commitments.channelId, expected = commitments.remoteNextHtlcId, actual = add.id) } - if (add.amountMsat < commitments.localParams.htlcMinimumMsat) { - throw HtlcValueTooSmall(commitments.channelId, minimum = commitments.localParams.htlcMinimumMsat, actual = add.amountMsat) + if (add.amountMsat < commitments.localParams.htlcMinimum) { + throw HtlcValueTooSmall(commitments.channelId, minimum = commitments.localParams.htlcMinimum, actual = add.amountMsat) } // let's compute the current commitment *as seen by us* including this change @@ -181,7 +183,7 @@ object Commitments { val reduced = CommitmentSpec.reduce(commitments1.localCommit.spec, commitments1.localChanges.acked, commitments1.remoteChanges.proposed) val incomingHtlcs = reduced.htlcs.filter(_.direction == IN) - val htlcValueInFlight = UInt64(incomingHtlcs.map(_.add.amountMsat).sum) + val htlcValueInFlight = UInt64(incomingHtlcs.map(_.add.amountMsat).sum.toLong) if (htlcValueInFlight > commitments1.localParams.maxHtlcValueInFlightMsat) { throw HtlcValueTooHighInFlight(commitments.channelId, maximum = commitments1.localParams.maxHtlcValueInFlightMsat, actual = htlcValueInFlight) } @@ -191,10 +193,10 @@ object Commitments { } // a node cannot spend pending incoming htlcs, and need to keep funds above the reserve required by the counterparty, after paying the fee - val fees = if (commitments1.localParams.isFunder) 0 else Transactions.commitTxFee(Satoshi(commitments1.localParams.dustLimitSatoshis), reduced).amount - val missing = reduced.toRemoteMsat / 1000 - commitments1.localParams.channelReserveSatoshis - fees - if (missing < 0) { - throw InsufficientFunds(commitments.channelId, amountMsat = add.amountMsat, missingSatoshis = -1 * missing, reserveSatoshis = commitments1.localParams.channelReserveSatoshis, feesSatoshis = fees) + val fees = if (commitments1.localParams.isFunder) Satoshi(0) else Transactions.commitTxFee(commitments1.localParams.dustLimit, reduced) + val missing = reduced.toRemote.truncateToSatoshi - commitments1.localParams.channelReserve - fees + if (missing < Satoshi(0)) { + throw InsufficientFunds(commitments.channelId, amount = add.amountMsat, missing = -missing, reserve = commitments1.localParams.channelReserve, fees = fees) } commitments1 @@ -313,10 +315,10 @@ object Commitments { // a node cannot spend pending incoming htlcs, and need to keep funds above the reserve required by the counterparty, after paying the fee // we look from remote's point of view, so if local is funder remote doesn't pay the fees - val fees = Transactions.commitTxFee(Satoshi(commitments1.remoteParams.dustLimitSatoshis), reduced).amount - val missing = reduced.toRemoteMsat / 1000 - commitments1.remoteParams.channelReserveSatoshis - fees - if (missing < 0) { - throw CannotAffordFees(commitments.channelId, missingSatoshis = -1 * missing, reserveSatoshis = commitments1.localParams.channelReserveSatoshis, feesSatoshis = fees) + val fees = commitTxFee(commitments1.remoteParams.dustLimit, reduced) + val missing = reduced.toRemote.truncateToSatoshi - commitments1.remoteParams.channelReserve - fees + if (missing < Satoshi(0)) { + throw CannotAffordFees(commitments.channelId, missing = -missing, reserve = commitments1.localParams.channelReserve, fees = fees) } (commitments1, fee) @@ -347,10 +349,10 @@ object Commitments { val reduced = CommitmentSpec.reduce(commitments1.localCommit.spec, commitments1.localChanges.acked, commitments1.remoteChanges.proposed) // a node cannot spend pending incoming htlcs, and need to keep funds above the reserve required by the counterparty, after paying the fee - val fees = Transactions.commitTxFee(Satoshi(commitments1.remoteParams.dustLimitSatoshis), reduced).amount - val missing = reduced.toRemoteMsat / 1000 - commitments1.localParams.channelReserveSatoshis - fees - if (missing < 0) { - throw CannotAffordFees(commitments.channelId, missingSatoshis = -1 * missing, reserveSatoshis = commitments1.localParams.channelReserveSatoshis, feesSatoshis = fees) + val fees = commitTxFee(commitments1.remoteParams.dustLimit, reduced) + val missing = reduced.toRemote.truncateToSatoshi - commitments1.localParams.channelReserve - fees + if (missing < Satoshi(0)) { + throw CannotAffordFees(commitments.channelId, missing = -missing, reserve = commitments1.localParams.channelReserve, fees = fees) } commitments1 @@ -527,8 +529,8 @@ object Commitments { val remotePaymentPubkey = Generators.derivePubKey(remoteParams.paymentBasepoint, localPerCommitmentPoint) val remoteHtlcPubkey = Generators.derivePubKey(remoteParams.htlcBasepoint, localPerCommitmentPoint) val localRevocationPubkey = Generators.revocationPubKey(remoteParams.revocationBasepoint, localPerCommitmentPoint) - val commitTx = Transactions.makeCommitTx(commitmentInput, commitTxNumber, keyManager.paymentPoint(localParams.channelKeyPath).publicKey, remoteParams.paymentBasepoint, localParams.isFunder, Satoshi(localParams.dustLimitSatoshis), localRevocationPubkey, remoteParams.toSelfDelay, localDelayedPaymentPubkey, remotePaymentPubkey, localHtlcPubkey, remoteHtlcPubkey, spec) - val (htlcTimeoutTxs, htlcSuccessTxs) = Transactions.makeHtlcTxs(commitTx.tx, Satoshi(localParams.dustLimitSatoshis), localRevocationPubkey, remoteParams.toSelfDelay, localDelayedPaymentPubkey, localHtlcPubkey, remoteHtlcPubkey, spec) + val commitTx = Transactions.makeCommitTx(commitmentInput, commitTxNumber, keyManager.paymentPoint(localParams.channelKeyPath).publicKey, remoteParams.paymentBasepoint, localParams.isFunder, localParams.dustLimit, localRevocationPubkey, remoteParams.toSelfDelay, localDelayedPaymentPubkey, remotePaymentPubkey, localHtlcPubkey, remoteHtlcPubkey, spec) + val (htlcTimeoutTxs, htlcSuccessTxs) = Transactions.makeHtlcTxs(commitTx.tx, localParams.dustLimit, localRevocationPubkey, remoteParams.toSelfDelay, localDelayedPaymentPubkey, localHtlcPubkey, remoteHtlcPubkey, spec) (commitTx, htlcTimeoutTxs, htlcSuccessTxs) } @@ -538,8 +540,8 @@ object Commitments { val remoteDelayedPaymentPubkey = Generators.derivePubKey(remoteParams.delayedPaymentBasepoint, remotePerCommitmentPoint) val remoteHtlcPubkey = Generators.derivePubKey(remoteParams.htlcBasepoint, remotePerCommitmentPoint) val remoteRevocationPubkey = Generators.revocationPubKey(keyManager.revocationPoint(localParams.channelKeyPath).publicKey, remotePerCommitmentPoint) - val commitTx = Transactions.makeCommitTx(commitmentInput, commitTxNumber, remoteParams.paymentBasepoint, keyManager.paymentPoint(localParams.channelKeyPath).publicKey, !localParams.isFunder, Satoshi(remoteParams.dustLimitSatoshis), remoteRevocationPubkey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, localPaymentPubkey, remoteHtlcPubkey, localHtlcPubkey, spec) - val (htlcTimeoutTxs, htlcSuccessTxs) = Transactions.makeHtlcTxs(commitTx.tx, Satoshi(remoteParams.dustLimitSatoshis), remoteRevocationPubkey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, remoteHtlcPubkey, localHtlcPubkey, spec) + val commitTx = Transactions.makeCommitTx(commitmentInput, commitTxNumber, remoteParams.paymentBasepoint, keyManager.paymentPoint(localParams.channelKeyPath).publicKey, !localParams.isFunder, remoteParams.dustLimit, remoteRevocationPubkey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, localPaymentPubkey, remoteHtlcPubkey, localHtlcPubkey, spec) + val (htlcTimeoutTxs, htlcSuccessTxs) = Transactions.makeHtlcTxs(commitTx.tx, remoteParams.dustLimit, remoteRevocationPubkey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, remoteHtlcPubkey, localHtlcPubkey, spec) (commitTx, htlcTimeoutTxs, htlcSuccessTxs) } @@ -574,18 +576,18 @@ object Commitments { def specs2String(commitments: Commitments): String = { s"""specs: |localcommit: - | toLocal: ${commitments.localCommit.spec.toLocalMsat} - | toRemote: ${commitments.localCommit.spec.toRemoteMsat} + | toLocal: ${commitments.localCommit.spec.toLocal} + | toRemote: ${commitments.localCommit.spec.toRemote} | htlcs: |${commitments.localCommit.spec.htlcs.map(h => s" ${h.direction} ${h.add.id} ${h.add.cltvExpiry}").mkString("\n")} |remotecommit: - | toLocal: ${commitments.remoteCommit.spec.toLocalMsat} - | toRemote: ${commitments.remoteCommit.spec.toRemoteMsat} + | toLocal: ${commitments.remoteCommit.spec.toLocal} + | toRemote: ${commitments.remoteCommit.spec.toRemote} | htlcs: |${commitments.remoteCommit.spec.htlcs.map(h => s" ${h.direction} ${h.add.id} ${h.add.cltvExpiry}").mkString("\n")} |next remotecommit: - | toLocal: ${commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.spec.toLocalMsat).getOrElse("N/A")} - | toRemote: ${commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.spec.toRemoteMsat).getOrElse("N/A")} + | toLocal: ${commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.spec.toLocal).getOrElse("N/A")} + | toRemote: ${commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.spec.toRemote).getOrElse("N/A")} | htlcs: |${commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.spec.htlcs.map(h => s" ${h.direction} ${h.add.id} ${h.add.cltvExpiry}").mkString("\n")).getOrElse("N/A")}""".stripMargin } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala index 5cbe075fcb..1f49e73429 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala @@ -20,6 +20,8 @@ import akka.event.LoggingAdapter import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey, ripemd160, sha256} import fr.acinq.bitcoin.Script._ import fr.acinq.bitcoin.{OutPoint, _} +import fr.acinq.eclair +import fr.acinq.eclair._ import fr.acinq.eclair.blockchain.EclairWallet import fr.acinq.eclair.blockchain.fee.{FeeEstimator, FeeTargets} import fr.acinq.eclair.channel.Channel.REFRESH_CHANNEL_UPDATE_INTERVAL @@ -88,10 +90,10 @@ object Helpers { // BOLT #2: if the chain_hash value, within the open_channel, message is set to a hash of a chain that is unknown to the receiver: // MUST reject the channel. if (nodeParams.chainHash != open.chainHash) throw InvalidChainHash(open.temporaryChannelId, local = nodeParams.chainHash, remote = open.chainHash) - if (open.fundingSatoshis < nodeParams.minFundingSatoshis || open.fundingSatoshis >= Channel.MAX_FUNDING_SATOSHIS) throw InvalidFundingAmount(open.temporaryChannelId, open.fundingSatoshis, nodeParams.minFundingSatoshis, Channel.MAX_FUNDING_SATOSHIS) + if (open.fundingSatoshis < nodeParams.minFundingSatoshis || open.fundingSatoshis >= Channel.MAX_FUNDING) throw InvalidFundingAmount(open.temporaryChannelId, open.fundingSatoshis, nodeParams.minFundingSatoshis, Channel.MAX_FUNDING) // BOLT #2: The receiving node MUST fail the channel if: push_msat is greater than funding_satoshis * 1000. - if (open.pushMsat > 1000 * open.fundingSatoshis) throw InvalidPushAmount(open.temporaryChannelId, open.pushMsat, 1000 * open.fundingSatoshis) + if (open.pushMsat > open.fundingSatoshis.toMilliSatoshi) throw InvalidPushAmount(open.temporaryChannelId, open.pushMsat, open.fundingSatoshis.toMilliSatoshi) // BOLT #2: The receiving node MUST fail the channel if: to_self_delay is unreasonably large. if (open.toSelfDelay > Channel.MAX_TO_SELF_DELAY || open.toSelfDelay > nodeParams.maxToLocalDelayBlocks) throw ToSelfDelayTooHigh(open.temporaryChannelId, open.toSelfDelay, nodeParams.maxToLocalDelayBlocks) @@ -107,8 +109,8 @@ object Helpers { // BOLT #2: The receiving node MUST fail the channel if both to_local and to_remote amounts for the initial commitment // transaction are less than or equal to channel_reserve_satoshis (see BOLT 3). - val (toLocalMsat, toRemoteMsat) = (open.pushMsat, open.fundingSatoshis * 1000 - open.pushMsat) - if (toLocalMsat < open.channelReserveSatoshis * 1000 && toRemoteMsat < open.channelReserveSatoshis * 1000) { + val (toLocalMsat, toRemoteMsat) = (open.pushMsat, open.fundingSatoshis.toMilliSatoshi - open.pushMsat) + if (toLocalMsat < open.channelReserveSatoshis.toMilliSatoshi && toRemoteMsat < open.channelReserveSatoshis.toMilliSatoshi) { throw ChannelReserveNotMet(open.temporaryChannelId, toLocalMsat, toRemoteMsat, open.channelReserveSatoshis) } @@ -122,7 +124,7 @@ object Helpers { // we don't check that the funder's amount for the initial commitment transaction is sufficient for full fee payment // now, but it will be done later when we receive `funding_created` - val reserveToFundingRatio = open.channelReserveSatoshis.toDouble / Math.max(open.fundingSatoshis, 1) + val reserveToFundingRatio = open.channelReserveSatoshis.toLong.toDouble / Math.max(open.fundingSatoshis.toLong, 1) if (reserveToFundingRatio > nodeParams.maxReserveToFundingRatio) throw ChannelReserveTooHigh(open.temporaryChannelId, open.channelReserveSatoshis, reserveToFundingRatio, nodeParams.maxReserveToFundingRatio) } @@ -151,7 +153,7 @@ object Helpers { // MUST reject the channel. Other fields have the same requirements as their counterparts in open_channel. if (open.channelReserveSatoshis < accept.dustLimitSatoshis) throw DustLimitAboveOurChannelReserve(accept.temporaryChannelId, accept.dustLimitSatoshis, open.channelReserveSatoshis) - val reserveToFundingRatio = accept.channelReserveSatoshis.toDouble / Math.max(open.fundingSatoshis, 1) + val reserveToFundingRatio = accept.channelReserveSatoshis.toLong.toDouble / Math.max(open.fundingSatoshis.toLong, 1) if (reserveToFundingRatio > nodeParams.maxReserveToFundingRatio) throw ChannelReserveTooHigh(open.temporaryChannelId, accept.channelReserveSatoshis, reserveToFundingRatio, nodeParams.maxReserveToFundingRatio) } @@ -219,10 +221,10 @@ object Helpers { case Left(waitingForRevocation) => waitingForRevocation.nextRemoteCommit case _ => commitments.remoteCommit } - val toRemoteSatoshis = remoteCommit.spec.toRemoteMsat / 1000 + val toRemoteSatoshis = remoteCommit.spec.toRemote.truncateToSatoshi // NB: this is an approximation (we don't take network fees into account) - val result = toRemoteSatoshis > commitments.remoteParams.channelReserveSatoshis - log.debug(s"toRemoteSatoshis=$toRemoteSatoshis reserve=${commitments.remoteParams.channelReserveSatoshis} aboveReserve=$result for remoteCommitNumber=${remoteCommit.index}") + val result = toRemoteSatoshis > commitments.remoteParams.channelReserve + log.debug(s"toRemoteSatoshis=$toRemoteSatoshis reserve=${commitments.remoteParams.channelReserve} aboveReserve=$result for remoteCommitNumber=${remoteCommit.index}") result } @@ -252,24 +254,24 @@ object Helpers { * @param remoteFirstPerCommitmentPoint * @return (localSpec, localTx, remoteSpec, remoteTx, fundingTxOutput) */ - def makeFirstCommitTxs(keyManager: KeyManager, temporaryChannelId: ByteVector32, localParams: LocalParams, remoteParams: RemoteParams, fundingSatoshis: Long, pushMsat: Long, initialFeeratePerKw: Long, fundingTxHash: ByteVector32, fundingTxOutputIndex: Int, remoteFirstPerCommitmentPoint: PublicKey, maxFeerateMismatch: Double): (CommitmentSpec, CommitTx, CommitmentSpec, CommitTx) = { - val toLocalMsat = if (localParams.isFunder) fundingSatoshis * 1000 - pushMsat else pushMsat - val toRemoteMsat = if (localParams.isFunder) pushMsat else fundingSatoshis * 1000 - pushMsat + def makeFirstCommitTxs(keyManager: KeyManager, temporaryChannelId: ByteVector32, localParams: LocalParams, remoteParams: RemoteParams, fundingAmount: Satoshi, pushMsat: MilliSatoshi, initialFeeratePerKw: Long, fundingTxHash: ByteVector32, fundingTxOutputIndex: Int, remoteFirstPerCommitmentPoint: PublicKey, maxFeerateMismatch: Double): (CommitmentSpec, CommitTx, CommitmentSpec, CommitTx) = { + val toLocalMsat = if (localParams.isFunder) fundingAmount.toMilliSatoshi - pushMsat else pushMsat + val toRemoteMsat = if (localParams.isFunder) pushMsat else fundingAmount.toMilliSatoshi - pushMsat - val localSpec = CommitmentSpec(Set.empty[DirectedHtlc], feeratePerKw = initialFeeratePerKw, toLocalMsat = toLocalMsat, toRemoteMsat = toRemoteMsat) - val remoteSpec = CommitmentSpec(Set.empty[DirectedHtlc], feeratePerKw = initialFeeratePerKw, toLocalMsat = toRemoteMsat, toRemoteMsat = toLocalMsat) + val localSpec = CommitmentSpec(Set.empty[DirectedHtlc], feeratePerKw = initialFeeratePerKw, toLocal = toLocalMsat, toRemote = toRemoteMsat) + val remoteSpec = CommitmentSpec(Set.empty[DirectedHtlc], feeratePerKw = initialFeeratePerKw, toLocal = toRemoteMsat, toRemote = toLocalMsat) if (!localParams.isFunder) { // they are funder, therefore they pay the fee: we need to make sure they can afford it! - val toRemoteMsat = remoteSpec.toLocalMsat - val fees = Transactions.commitTxFee(Satoshi(remoteParams.dustLimitSatoshis), remoteSpec).amount - val missing = toRemoteMsat / 1000 - localParams.channelReserveSatoshis - fees - if (missing < 0) { - throw CannotAffordFees(temporaryChannelId, missingSatoshis = -1 * missing, reserveSatoshis = localParams.channelReserveSatoshis, feesSatoshis = fees) + val toRemoteMsat = remoteSpec.toLocal + val fees = commitTxFee(remoteParams.dustLimit, remoteSpec) + val missing = toRemoteMsat.truncateToSatoshi - localParams.channelReserve - fees + if (missing < Satoshi(0)) { + throw CannotAffordFees(temporaryChannelId, missing = -missing, reserve = localParams.channelReserve, fees = fees) } } - val commitmentInput = makeFundingInputInfo(fundingTxHash, fundingTxOutputIndex, Satoshi(fundingSatoshis), keyManager.fundingPublicKey(localParams.channelKeyPath).publicKey, remoteParams.fundingPubKey) + val commitmentInput = makeFundingInputInfo(fundingTxHash, fundingTxOutputIndex, fundingAmount, keyManager.fundingPublicKey(localParams.channelKeyPath).publicKey, remoteParams.fundingPubKey) val localPerCommitmentPoint = keyManager.commitmentPoint(localParams.channelKeyPath, 0) val (localCommitTx, _, _) = Commitments.makeLocalTxs(keyManager, 0, localParams, remoteParams, commitmentInput, localPerCommitmentPoint, localSpec) val (remoteCommitTx, _, _) = Commitments.makeRemoteTxs(keyManager, 0, localParams, remoteParams, commitmentInput, remoteFirstPerCommitmentPoint, remoteSpec) @@ -358,9 +360,9 @@ object Helpers { */ def nothingAtStake(data: HasCommitments): Boolean = data.commitments.localCommit.index == 0 && - data.commitments.localCommit.spec.toLocalMsat == 0 && + data.commitments.localCommit.spec.toLocal == MilliSatoshi(0) && data.commitments.remoteCommit.index == 0 && - data.commitments.remoteCommit.spec.toRemoteMsat == 0 && + data.commitments.remoteCommit.spec.toRemote == MilliSatoshi(0) && data.commitments.remoteNextCommitInfo.isRight /** @@ -453,10 +455,10 @@ object Helpers { require(isValidFinalScriptPubkey(remoteScriptPubkey), "invalid remoteScriptPubkey") log.debug(s"making closing tx with closingFee={} and commitments:\n{}", closingFee, Commitments.specs2String(commitments)) // TODO: check that - val dustLimitSatoshis = Satoshi(Math.max(localParams.dustLimitSatoshis, remoteParams.dustLimitSatoshis)) + val dustLimitSatoshis = maxOf(localParams.dustLimit, remoteParams.dustLimit) val closingTx = Transactions.makeClosingTx(commitInput, localScriptPubkey, remoteScriptPubkey, localParams.isFunder, dustLimitSatoshis, closingFee, localCommit.spec) val localClosingSig = keyManager.sign(closingTx, keyManager.fundingPublicKey(commitments.localParams.channelKeyPath)) - val closingSigned = ClosingSigned(channelId, closingFee.amount, localClosingSig) + val closingSigned = ClosingSigned(channelId, closingFee, localClosingSig) log.info(s"signed closing txid=${closingTx.tx.txid} with closingFeeSatoshis=${closingSigned.feeSatoshis}") log.debug(s"closingTxid=${closingTx.tx.txid} closingTx=${closingTx.tx}}") (closingTx, closingSigned) @@ -467,7 +469,7 @@ object Helpers { val lastCommitFeeSatoshi = commitments.commitInput.txOut.amount.amount - commitments.localCommit.publishableTxs.commitTx.tx.txOut.map(_.amount.amount).sum if (remoteClosingFee.amount > lastCommitFeeSatoshi) { log.error(s"remote proposed a commit fee higher than the last commitment fee: remoteClosingFeeSatoshi=${remoteClosingFee.amount} lastCommitFeeSatoshi=$lastCommitFeeSatoshi") - throw InvalidCloseFee(commitments.channelId, remoteClosingFee.amount) + throw InvalidCloseFee(commitments.channelId, remoteClosingFee) } val (closingTx, closingSigned) = makeClosingTx(keyManager, commitments, localScriptPubkey, remoteScriptPubkey, remoteClosingFee) val signedClosingTx = Transactions.addSigs(closingTx, keyManager.fundingPublicKey(commitments.localParams.channelKeyPath).publicKey, remoteParams.fundingPubKey, closingSigned.signature, remoteClosingSig) @@ -508,7 +510,7 @@ object Helpers { // first we will claim our main output as soon as the delay is over val mainDelayedTx = generateTx("main-delayed-output")(Try { - val claimDelayed = Transactions.makeClaimDelayedOutputTx(tx, Satoshi(localParams.dustLimitSatoshis), localRevocationPubkey, remoteParams.toSelfDelay, localDelayedPubkey, localParams.defaultFinalScriptPubKey, feeratePerKwDelayed) + val claimDelayed = Transactions.makeClaimDelayedOutputTx(tx, localParams.dustLimit, localRevocationPubkey, remoteParams.toSelfDelay, localDelayedPubkey, localParams.defaultFinalScriptPubKey, feeratePerKwDelayed) val sig = keyManager.sign(claimDelayed, keyManager.delayedPaymentPoint(localParams.channelKeyPath), localPerCommitmentPoint) Transactions.addSigs(claimDelayed, sig) }) @@ -539,7 +541,7 @@ object Helpers { generateTx("claim-htlc-delayed")(Try { val claimDelayed = Transactions.makeClaimDelayedOutputTx( txinfo.tx, - Satoshi(localParams.dustLimitSatoshis), + localParams.dustLimit, localRevocationPubkey, remoteParams.toSelfDelay, localDelayedPubkey, @@ -590,7 +592,7 @@ object Helpers { // incoming htlc for which we have the preimage: we spend it directly case DirectedHtlc(OUT, add: UpdateAddHtlc) if preimages.exists(r => sha256(r) == add.paymentHash) => generateTx("claim-htlc-success")(Try { val preimage = preimages.find(r => sha256(r) == add.paymentHash).get - val txinfo = Transactions.makeClaimHtlcSuccessTx(remoteCommitTx.tx, outputsAlreadyUsed, Satoshi(localParams.dustLimitSatoshis), localHtlcPubkey, remoteHtlcPubkey, remoteRevocationPubkey, localParams.defaultFinalScriptPubKey, add, feeratePerKwHtlc) + val txinfo = Transactions.makeClaimHtlcSuccessTx(remoteCommitTx.tx, outputsAlreadyUsed, localParams.dustLimit, localHtlcPubkey, remoteHtlcPubkey, remoteRevocationPubkey, localParams.defaultFinalScriptPubKey, add, feeratePerKwHtlc) outputsAlreadyUsed = outputsAlreadyUsed + txinfo.input.outPoint.index.toInt val sig = keyManager.sign(txinfo, keyManager.htlcPoint(localParams.channelKeyPath), remoteCommit.remotePerCommitmentPoint) Transactions.addSigs(txinfo, sig, preimage) @@ -600,7 +602,7 @@ object Helpers { // outgoing htlc: they may or may not have the preimage, the only thing to do is try to get back our funds after timeout case DirectedHtlc(IN, add: UpdateAddHtlc) => generateTx("claim-htlc-timeout")(Try { - val txinfo = Transactions.makeClaimHtlcTimeoutTx(remoteCommitTx.tx, outputsAlreadyUsed, Satoshi(localParams.dustLimitSatoshis), localHtlcPubkey, remoteHtlcPubkey, remoteRevocationPubkey, localParams.defaultFinalScriptPubKey, add, feeratePerKwHtlc) + val txinfo = Transactions.makeClaimHtlcTimeoutTx(remoteCommitTx.tx, outputsAlreadyUsed, localParams.dustLimit, localHtlcPubkey, remoteHtlcPubkey, remoteRevocationPubkey, localParams.defaultFinalScriptPubKey, add, feeratePerKwHtlc) outputsAlreadyUsed = outputsAlreadyUsed + txinfo.input.outPoint.index.toInt val sig = keyManager.sign(txinfo, keyManager.htlcPoint(localParams.channelKeyPath), remoteCommit.remotePerCommitmentPoint) Transactions.addSigs(txinfo, sig) @@ -630,7 +632,7 @@ object Helpers { val feeratePerKwMain = feeEstimator.getFeeratePerKw(feeTargets.claimMainBlockTarget) val mainTx = generateTx("claim-p2wpkh-output")(Try { - val claimMain = Transactions.makeClaimP2WPKHOutputTx(tx, Satoshi(commitments.localParams.dustLimitSatoshis), + val claimMain = Transactions.makeClaimP2WPKHOutputTx(tx, commitments.localParams.dustLimit, localPubkey, commitments.localParams.defaultFinalScriptPubKey, feeratePerKwMain) val sig = keyManager.sign(claimMain, keyManager.paymentPoint(commitments.localParams.channelKeyPath), remotePerCommitmentPoint) Transactions.addSigs(claimMain, localPubkey, sig) @@ -679,14 +681,14 @@ object Helpers { // first we will claim our main output right away val mainTx = generateTx("claim-p2wpkh-output")(Try { - val claimMain = Transactions.makeClaimP2WPKHOutputTx(tx, Satoshi(localParams.dustLimitSatoshis), localPubkey, localParams.defaultFinalScriptPubKey, feeratePerKwMain) + val claimMain = Transactions.makeClaimP2WPKHOutputTx(tx, localParams.dustLimit, localPubkey, localParams.defaultFinalScriptPubKey, feeratePerKwMain) val sig = keyManager.sign(claimMain, keyManager.paymentPoint(localParams.channelKeyPath), remotePerCommitmentPoint) Transactions.addSigs(claimMain, localPubkey, sig) }) // then we punish them by stealing their main output val mainPenaltyTx = generateTx("main-penalty")(Try { - val txinfo = Transactions.makeMainPenaltyTx(tx, Satoshi(localParams.dustLimitSatoshis), remoteRevocationPubkey, localParams.defaultFinalScriptPubKey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, feeratePerKwPenalty) + val txinfo = Transactions.makeMainPenaltyTx(tx, localParams.dustLimit, remoteRevocationPubkey, localParams.defaultFinalScriptPubKey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, feeratePerKwPenalty) val sig = keyManager.sign(txinfo, keyManager.revocationPoint(localParams.channelKeyPath), remotePerCommitmentSecret) Transactions.addSigs(txinfo, sig) }) @@ -706,7 +708,7 @@ object Helpers { val htlcPenaltyTxs = tx.txOut.collect { case txOut if htlcsRedeemScripts.contains(txOut.publicKeyScript) => val htlcRedeemScript = htlcsRedeemScripts(txOut.publicKeyScript) generateTx("htlc-penalty")(Try { - val htlcPenalty = Transactions.makeHtlcPenaltyTx(tx, outputsAlreadyUsed, htlcRedeemScript, Satoshi(localParams.dustLimitSatoshis), localParams.defaultFinalScriptPubKey, feeratePerKwPenalty) + val htlcPenalty = Transactions.makeHtlcPenaltyTx(tx, outputsAlreadyUsed, htlcRedeemScript, localParams.dustLimit, localParams.defaultFinalScriptPubKey, feeratePerKwPenalty) outputsAlreadyUsed = outputsAlreadyUsed + htlcPenalty.input.outPoint.index.toInt val sig = keyManager.sign(htlcPenalty, keyManager.revocationPoint(localParams.channelKeyPath), remotePerCommitmentSecret) Transactions.addSigs(htlcPenalty, sig, remoteRevocationPubkey) @@ -762,7 +764,7 @@ object Helpers { val feeratePerKwPenalty = feeEstimator.getFeeratePerKw(target = 1) generateTx("claim-htlc-delayed-penalty")(Try { - val htlcDelayedPenalty = Transactions.makeClaimDelayedOutputPenaltyTx(htlcTx, Satoshi(localParams.dustLimitSatoshis), remoteRevocationPubkey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, localParams.defaultFinalScriptPubKey, feeratePerKwPenalty) + val htlcDelayedPenalty = Transactions.makeClaimDelayedOutputPenaltyTx(htlcTx, localParams.dustLimit, remoteRevocationPubkey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, localParams.defaultFinalScriptPubKey, feeratePerKwPenalty) val sig = keyManager.sign(htlcDelayedPenalty, keyManager.revocationPoint(localParams.channelKeyPath), remotePerCommitmentSecret) val signedTx = Transactions.addSigs(htlcDelayedPenalty, sig) // we need to make sure that the tx is indeed valid diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/AuditDb.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/AuditDb.scala index 63dbe4d74f..5fccd441b1 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/AuditDb.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/AuditDb.scala @@ -16,7 +16,7 @@ package fr.acinq.eclair.db -import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.eclair.channel._ import fr.acinq.eclair.payment.{PaymentReceived, PaymentRelayed, PaymentSent} @@ -51,8 +51,8 @@ trait AuditDb { } -case class ChannelLifecycleEvent(channelId: ByteVector32, remoteNodeId: PublicKey, capacitySat: Long, isFunder: Boolean, isPrivate: Boolean, event: String) +case class ChannelLifecycleEvent(channelId: ByteVector32, remoteNodeId: PublicKey, capacity: Satoshi, isFunder: Boolean, isPrivate: Boolean, event: String) -case class NetworkFee(remoteNodeId: PublicKey, channelId: ByteVector32, txId: ByteVector32, feeSat: Long, txType: String, timestamp: Long) +case class NetworkFee(remoteNodeId: PublicKey, channelId: ByteVector32, txId: ByteVector32, fee: Satoshi, txType: String, timestamp: Long) -case class Stats(channelId: ByteVector32, avgPaymentAmountSatoshi: Long, paymentCount: Int, relayFeeSatoshi: Long, networkFeeSatoshi: Long) +case class Stats(channelId: ByteVector32, avgPaymentAmount: Satoshi, paymentCount: Int, relayFee: Satoshi, networkFee: Satoshi) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/PaymentsDb.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/PaymentsDb.scala index b13e190446..b9f4aa838f 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/PaymentsDb.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/PaymentsDb.scala @@ -17,7 +17,9 @@ package fr.acinq.eclair.db import java.util.UUID + import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.eclair.MilliSatoshi import fr.acinq.eclair.payment.PaymentRequest trait PaymentsDb { @@ -60,10 +62,10 @@ trait PaymentsDb { * Incoming payment object stored in DB. * * @param paymentHash identifier of the payment - * @param amountMsat amount of the payment, in milli-satoshis + * @param amount amount of the payment, in milli-satoshis * @param receivedAt absolute time in seconds since UNIX epoch when the payment was received. */ -case class IncomingPayment(paymentHash: ByteVector32, amountMsat: Long, receivedAt: Long) +case class IncomingPayment(paymentHash: ByteVector32, amount: MilliSatoshi, receivedAt: Long) /** * Sent payment is every payment that is sent by this node, they may not be finalized and @@ -72,12 +74,12 @@ case class IncomingPayment(paymentHash: ByteVector32, amountMsat: Long, received * @param id internal payment identifier * @param paymentHash payment_hash * @param preimage the preimage of the payment_hash, known if the outgoing payment was successful - * @param amountMsat amount of the payment, in milli-satoshis + * @param amount amount of the payment, in milli-satoshis * @param createdAt absolute time in seconds since UNIX epoch when the payment was created. * @param completedAt absolute time in seconds since UNIX epoch when the payment succeeded. * @param status current status of the payment. */ -case class OutgoingPayment(id: UUID, paymentHash: ByteVector32, preimage:Option[ByteVector32], amountMsat: Long, createdAt: Long, completedAt: Option[Long], status: OutgoingPaymentStatus.Value) +case class OutgoingPayment(id: UUID, paymentHash: ByteVector32, preimage:Option[ByteVector32], amount: MilliSatoshi, createdAt: Long, completedAt: Option[Long], status: OutgoingPaymentStatus.Value) object OutgoingPaymentStatus extends Enumeration { val PENDING = Value(1, "PENDING") diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqliteAuditDb.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqliteAuditDb.scala index 519ed96b8d..c03bec2a84 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqliteAuditDb.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqliteAuditDb.scala @@ -19,7 +19,8 @@ package fr.acinq.eclair.db.sqlite import java.sql.{Connection, Statement} import java.util.UUID import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.MilliSatoshi +import fr.acinq.bitcoin.Satoshi +import fr.acinq.eclair.MilliSatoshi import fr.acinq.eclair.channel.{AvailableBalanceChanged, Channel, ChannelErrorOccured, NetworkFeePaid} import fr.acinq.eclair.db.{AuditDb, ChannelLifecycleEvent, NetworkFee, Stats} import fr.acinq.eclair.payment.{PaymentReceived, PaymentRelayed, PaymentSent} @@ -83,9 +84,9 @@ class SqliteAuditDb(sqlite: Connection) extends AuditDb with Logging { using(sqlite.prepareStatement("INSERT INTO balance_updated VALUES (?, ?, ?, ?, ?, ?)")) { statement => statement.setBytes(1, e.channelId.toArray) statement.setBytes(2, e.commitments.remoteParams.nodeId.value.toArray) - statement.setLong(3, e.localBalanceMsat) + statement.setLong(3, e.localBalance.toLong) statement.setLong(4, e.commitments.commitInput.txOut.amount.toLong) - statement.setLong(5, e.commitments.remoteParams.channelReserveSatoshis) // remote decides what our reserve should be + statement.setLong(5, e.commitments.remoteParams.channelReserve.toLong) // remote decides what our reserve should be statement.setLong(6, Platform.currentTime) statement.executeUpdate() } @@ -94,7 +95,7 @@ class SqliteAuditDb(sqlite: Connection) extends AuditDb with Logging { using(sqlite.prepareStatement("INSERT INTO channel_events VALUES (?, ?, ?, ?, ?, ?, ?)")) { statement => statement.setBytes(1, e.channelId.toArray) statement.setBytes(2, e.remoteNodeId.value.toArray) - statement.setLong(3, e.capacitySat) + statement.setLong(3, e.capacity.toLong) statement.setBoolean(4, e.isFunder) statement.setBoolean(5, e.isPrivate) statement.setString(6, e.event) @@ -225,7 +226,7 @@ class SqliteAuditDb(sqlite: Connection) extends AuditDb with Logging { remoteNodeId = PublicKey(rs.getByteVector("node_id")), channelId = rs.getByteVector32("channel_id"), txId = rs.getByteVector32("tx_id"), - feeSat = rs.getLong("fee_sat"), + fee = Satoshi(rs.getLong("fee_sat")), txType = rs.getString("tx_type"), timestamp = rs.getLong("timestamp")) } @@ -267,10 +268,10 @@ class SqliteAuditDb(sqlite: Connection) extends AuditDb with Logging { while (rs.next()) { q = q :+ Stats( channelId = rs.getByteVector32("channel_id"), - avgPaymentAmountSatoshi = rs.getLong("avg_payment_amount_sat"), + avgPaymentAmount = Satoshi(rs.getLong("avg_payment_amount_sat")), paymentCount = rs.getInt("payment_count"), - relayFeeSatoshi = rs.getLong("relay_fee_sat"), - networkFeeSatoshi = rs.getLong("network_fee_sat")) + relayFee = Satoshi(rs.getLong("relay_fee_sat")), + networkFee = Satoshi(rs.getLong("network_fee_sat"))) } q } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqlitePaymentsDb.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqlitePaymentsDb.scala index 8fd6ded566..127ec6be2e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqlitePaymentsDb.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqlitePaymentsDb.scala @@ -25,6 +25,7 @@ import fr.acinq.eclair.payment.PaymentRequest import grizzled.slf4j.Logging import scala.collection.immutable.Queue import OutgoingPaymentStatus._ +import fr.acinq.eclair.MilliSatoshi import concurrent.duration._ import scala.compat.Platform @@ -47,7 +48,7 @@ class SqlitePaymentsDb(sqlite: Connection) extends PaymentsDb with Logging { using(sqlite.prepareStatement("INSERT INTO sent_payments (id, payment_hash, amount_msat, created_at, status) VALUES (?, ?, ?, ?, ?)")) { statement => statement.setString(1, sent.id.toString) statement.setBytes(2, sent.paymentHash.toArray) - statement.setLong(3, sent.amountMsat) + statement.setLong(3, sent.amount.toLong) statement.setLong(4, sent.createdAt) statement.setString(5, sent.status.toString) val res = statement.executeUpdate() @@ -76,7 +77,7 @@ class SqlitePaymentsDb(sqlite: Connection) extends PaymentsDb with Logging { UUID.fromString(rs.getString("id")), rs.getByteVector32("payment_hash"), rs.getByteVector32Nullable("preimage"), - rs.getLong("amount_msat"), + MilliSatoshi(rs.getLong("amount_msat")), rs.getLong("created_at"), getNullableLong(rs, "completed_at"), OutgoingPaymentStatus.withName(rs.getString("status")) @@ -97,7 +98,7 @@ class SqlitePaymentsDb(sqlite: Connection) extends PaymentsDb with Logging { UUID.fromString(rs.getString("id")), rs.getByteVector32("payment_hash"), rs.getByteVector32Nullable("preimage"), - rs.getLong("amount_msat"), + MilliSatoshi(rs.getLong("amount_msat")), rs.getLong("created_at"), getNullableLong(rs, "completed_at"), OutgoingPaymentStatus.withName(rs.getString("status")) @@ -116,7 +117,7 @@ class SqlitePaymentsDb(sqlite: Connection) extends PaymentsDb with Logging { UUID.fromString(rs.getString("id")), rs.getByteVector32("payment_hash"), rs.getByteVector32Nullable("preimage"), - rs.getLong("amount_msat"), + MilliSatoshi(rs.getLong("amount_msat")), rs.getLong("created_at"), getNullableLong(rs, "completed_at"), OutgoingPaymentStatus.withName(rs.getString("status")) @@ -194,7 +195,7 @@ class SqlitePaymentsDb(sqlite: Connection) extends PaymentsDb with Logging { override def addIncomingPayment(payment: IncomingPayment): Unit = { using(sqlite.prepareStatement("UPDATE received_payments SET (received_msat, received_at) = (?, ?) WHERE payment_hash = ?")) { statement => - statement.setLong(1, payment.amountMsat) + statement.setLong(1, payment.amount.toLong) statement.setLong(2, payment.receivedAt) statement.setBytes(3, payment.paymentHash.toArray) val res = statement.executeUpdate() @@ -207,7 +208,7 @@ class SqlitePaymentsDb(sqlite: Connection) extends PaymentsDb with Logging { statement.setBytes(1, paymentHash.toArray) val rs = statement.executeQuery() if (rs.next()) { - Some(IncomingPayment(rs.getByteVector32("payment_hash"), rs.getLong("received_msat"), rs.getLong("received_at"))) + Some(IncomingPayment(rs.getByteVector32("payment_hash"), MilliSatoshi(rs.getLong("received_msat")), rs.getLong("received_at"))) } else { None } @@ -219,7 +220,7 @@ class SqlitePaymentsDb(sqlite: Connection) extends PaymentsDb with Logging { val rs = statement.executeQuery("SELECT payment_hash, received_msat, received_at FROM received_payments WHERE received_msat > 0") var q: Queue[IncomingPayment] = Queue() while (rs.next()) { - q = q :+ IncomingPayment(rs.getByteVector32("payment_hash"), rs.getLong("received_msat"), rs.getLong("received_at")) + q = q :+ IncomingPayment(rs.getByteVector32("payment_hash"), MilliSatoshi(rs.getLong("received_msat")), rs.getLong("received_at")) } q } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala b/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala index dbc91f208e..3f672d2e71 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala @@ -25,7 +25,8 @@ import akka.event.Logging.MDC import akka.util.Timeout import com.google.common.net.HostAndPort import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{ByteVector32, DeterministicWallet, MilliSatoshi, Protocol, Satoshi} +import fr.acinq.bitcoin.{ByteVector32, DeterministicWallet, Protocol, Satoshi} +import fr.acinq.eclair import fr.acinq.eclair.blockchain.EclairWallet import fr.acinq.eclair.channel._ import fr.acinq.eclair.crypto.TransportHandler @@ -280,20 +281,20 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A stay case Event(c: Peer.OpenChannel, d: ConnectedData) => - val (channel, localParams) = createNewChannel(nodeParams, funder = true, c.fundingSatoshis.toLong, origin_opt = Some(sender)) + val (channel, localParams) = createNewChannel(nodeParams, funder = true, c.fundingSatoshis, origin_opt = Some(sender)) c.timeout_opt.map(openTimeout => context.system.scheduler.scheduleOnce(openTimeout.duration, channel, Channel.TickChannelOpenTimeout)(context.dispatcher)) val temporaryChannelId = randomBytes32 val channelFeeratePerKw = nodeParams.onChainFeeConf.feeEstimator.getFeeratePerKw(target = nodeParams.onChainFeeConf.feeTargets.commitmentBlockTarget) val fundingTxFeeratePerKw = c.fundingTxFeeratePerKw_opt.getOrElse(nodeParams.onChainFeeConf.feeEstimator.getFeeratePerKw(target = nodeParams.onChainFeeConf.feeTargets.fundingBlockTarget)) log.info(s"requesting a new channel with fundingSatoshis=${c.fundingSatoshis}, pushMsat=${c.pushMsat} and fundingFeeratePerByte=${c.fundingTxFeeratePerKw_opt} temporaryChannelId=$temporaryChannelId localParams=$localParams") - channel ! INPUT_INIT_FUNDER(temporaryChannelId, c.fundingSatoshis.amount, c.pushMsat.amount, channelFeeratePerKw, fundingTxFeeratePerKw, localParams, d.transport, d.remoteInit, c.channelFlags.getOrElse(nodeParams.channelFlags)) + channel ! INPUT_INIT_FUNDER(temporaryChannelId, c.fundingSatoshis, c.pushMsat, channelFeeratePerKw, fundingTxFeeratePerKw, localParams, d.transport, d.remoteInit, c.channelFlags.getOrElse(nodeParams.channelFlags)) stay using d.copy(channels = d.channels + (TemporaryChannelId(temporaryChannelId) -> channel)) case Event(msg: wire.OpenChannel, d: ConnectedData) => d.transport ! TransportHandler.ReadAck(msg) d.channels.get(TemporaryChannelId(msg.temporaryChannelId)) match { case None => - val (channel, localParams) = createNewChannel(nodeParams, funder = false, fundingSatoshis = msg.fundingSatoshis, origin_opt = None) + val (channel, localParams) = createNewChannel(nodeParams, funder = false, fundingAmount = msg.fundingSatoshis, origin_opt = None) val temporaryChannelId = msg.temporaryChannelId log.info(s"accepting a new channel to $remoteNodeId temporaryChannelId=$temporaryChannelId localParams=$localParams") channel ! INPUT_INIT_FUNDEE(temporaryChannelId, localParams, d.transport, d.remoteInit) @@ -523,9 +524,9 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A case DISCONNECTED -> _ if nodeParams.autoReconnect => cancelTimer(RECONNECT_TIMER) } - def createNewChannel(nodeParams: NodeParams, funder: Boolean, fundingSatoshis: Long, origin_opt: Option[ActorRef]): (ActorRef, LocalParams) = { + def createNewChannel(nodeParams: NodeParams, funder: Boolean, fundingAmount: Satoshi, origin_opt: Option[ActorRef]): (ActorRef, LocalParams) = { val defaultFinalScriptPubKey = Helpers.getFinalScriptPubKey(wallet, nodeParams.chainHash) - val localParams = makeChannelParams(nodeParams, defaultFinalScriptPubKey, funder, fundingSatoshis) + val localParams = makeChannelParams(nodeParams, defaultFinalScriptPubKey, funder, fundingAmount) val channel = spawnChannel(nodeParams, origin_opt) (channel, localParams) } @@ -605,7 +606,7 @@ object Peer { case class Disconnect(nodeId: PublicKey) case object ResumeAnnouncements case class OpenChannel(remoteNodeId: PublicKey, fundingSatoshis: Satoshi, pushMsat: MilliSatoshi, fundingTxFeeratePerKw_opt: Option[Long], channelFlags: Option[Byte], timeout_opt: Option[Timeout]) { - require(fundingSatoshis.amount < Channel.MAX_FUNDING_SATOSHIS, s"fundingSatoshis must be less than ${Channel.MAX_FUNDING_SATOSHIS}") + require(fundingSatoshis < Channel.MAX_FUNDING, s"fundingSatoshis must be less than ${Channel.MAX_FUNDING}") require(pushMsat.amount <= 1000 * fundingSatoshis.amount, s"pushMsat must be less or equal to fundingSatoshis") require(fundingSatoshis.amount >= 0, s"fundingSatoshis must be positive") require(pushMsat.amount >= 0, s"pushMsat must be positive") @@ -628,22 +629,22 @@ object Peer { // @formatter:on - def makeChannelParams(nodeParams: NodeParams, defaultFinalScriptPubKey: ByteVector, isFunder: Boolean, fundingSatoshis: Long): LocalParams = { + def makeChannelParams(nodeParams: NodeParams, defaultFinalScriptPubKey: ByteVector, isFunder: Boolean, fundingAmount: Satoshi): LocalParams = { val entropy = new Array[Byte](16) secureRandom.nextBytes(entropy) val bis = new ByteArrayInputStream(entropy) val channelKeyPath = DeterministicWallet.KeyPath(Seq(Protocol.uint32(bis, ByteOrder.BIG_ENDIAN), Protocol.uint32(bis, ByteOrder.BIG_ENDIAN), Protocol.uint32(bis, ByteOrder.BIG_ENDIAN), Protocol.uint32(bis, ByteOrder.BIG_ENDIAN))) - makeChannelParams(nodeParams, defaultFinalScriptPubKey, isFunder, fundingSatoshis, channelKeyPath) + makeChannelParams(nodeParams, defaultFinalScriptPubKey, isFunder, fundingAmount, channelKeyPath) } - def makeChannelParams(nodeParams: NodeParams, defaultFinalScriptPubKey: ByteVector, isFunder: Boolean, fundingSatoshis: Long, channelKeyPath: DeterministicWallet.KeyPath): LocalParams = { + def makeChannelParams(nodeParams: NodeParams, defaultFinalScriptPubKey: ByteVector, isFunder: Boolean, fundingAmount: Satoshi, channelKeyPath: DeterministicWallet.KeyPath): LocalParams = { LocalParams( nodeParams.nodeId, channelKeyPath, - dustLimitSatoshis = nodeParams.dustLimitSatoshis, + dustLimit = nodeParams.dustLimit, maxHtlcValueInFlightMsat = nodeParams.maxHtlcValueInFlightMsat, - channelReserveSatoshis = Math.max((nodeParams.reserveToFundingRatio * fundingSatoshis).toLong, nodeParams.dustLimitSatoshis), // BOLT #2: make sure that our reserve is above our dust limit - htlcMinimumMsat = nodeParams.htlcMinimumMsat, + channelReserve = maxOf(Satoshi((nodeParams.reserveToFundingRatio * fundingAmount.toLong).toLong), nodeParams.dustLimit), // BOLT #2: make sure that our reserve is above our dust limit + htlcMinimum = nodeParams.htlcMinimum, toSelfDelay = nodeParams.toRemoteDelayBlocks, // we choose their delay maxAcceptedHtlcs = nodeParams.maxAcceptedHtlcs, defaultFinalScriptPubKey = defaultFinalScriptPubKey, diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/package.scala b/eclair-core/src/main/scala/fr/acinq/eclair/package.scala index 44adabf9eb..3ae5f2ffed 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/package.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/package.scala @@ -126,7 +126,7 @@ package object eclair { * @param msat amount in millisatoshi * @return the fee (in msat) that a node should be paid to forward an HTLC of 'amount' millisatoshis */ - def nodeFee(baseMsat: Long, proportional: Long, msat: Long): Long = baseMsat + (proportional * msat) / 1000000 + def nodeFee(baseMsat: MilliSatoshi, proportional: Long, msat: MilliSatoshi): MilliSatoshi = baseMsat + (msat * proportional) / 1000000 /** * @@ -159,4 +159,54 @@ package object eclair { * We use this in the context of timestamp filtering, when we don't need an upper bound. */ val MaxEpochSeconds = Duration.fromNanos(Long.MaxValue).toSeconds + + /** + * One MilliSatoshi is a thousand of a Satoshi, the smallest unit usable in bitcoin + * @param amount + */ + case class MilliSatoshi(amount: Long) { + // @formatter:off + def toLong = amount + def +(other: MilliSatoshi) = MilliSatoshi(amount + other.amount) + def -(other: MilliSatoshi) = MilliSatoshi(amount - other.amount) + def *(m: Long) = MilliSatoshi(amount * m) + def /(d: Long) = MilliSatoshi(amount / d) + def compare(other: MilliSatoshi): Int = if (amount == other.amount) 0 else if (amount < other.amount) -1 else 1 + def <= (that: MilliSatoshi): Boolean = compare(that) <= 0 + def >= (that: MilliSatoshi): Boolean = compare(that) >= 0 + def < (that: MilliSatoshi): Boolean = compare(that) < 0 + def > (that: MilliSatoshi): Boolean = compare(that) > 0 + def unary_-() = MilliSatoshi(-amount) + def truncateToSatoshi: Satoshi = Satoshi(amount / 1000) + // @formatter:on + } + + implicit class ToMilliSatoshiConversion(amount: BtcAmount) { + def toMilliSatoshi: MilliSatoshi = amount match { + case sat: Satoshi => satoshi2millisatoshi(sat) + case millis: MilliBtc => satoshi2millisatoshi(millibtc2satoshi(millis)) + case bitcoin: Btc => satoshi2millisatoshi(btc2satoshi(bitcoin)) + } + } + + implicit object NumericMilliSatoshi extends Numeric[MilliSatoshi] { + override def plus(x: MilliSatoshi, y: MilliSatoshi): MilliSatoshi = MilliSatoshi(x.amount + y.amount) + override def minus(x: MilliSatoshi, y: MilliSatoshi): MilliSatoshi = MilliSatoshi(x.amount - y.amount) + override def times(x: MilliSatoshi, y: MilliSatoshi): MilliSatoshi = MilliSatoshi(x.amount * y.amount) + override def negate(x: MilliSatoshi): MilliSatoshi = MilliSatoshi(-x.amount) + override def fromInt(x: Int): MilliSatoshi = MilliSatoshi(x) + override def toInt(x: MilliSatoshi): Int = x.toLong.toInt + override def toLong(x: MilliSatoshi): Long = x.toLong + override def toFloat(x: MilliSatoshi): Float = x.toLong + override def toDouble(x: MilliSatoshi): Double = x.toLong + override def compare(x: MilliSatoshi, y: MilliSatoshi): Int = x.compare(y) + } + + private def satoshi2millisatoshi(input: Satoshi): MilliSatoshi = MilliSatoshi(input.amount * 1000L) + + def maxOf(x: MilliSatoshi, y: MilliSatoshi) = MilliSatoshi(Math.max(x.amount, y.amount)) + def minOf(x: MilliSatoshi, y: MilliSatoshi) = MilliSatoshi(Math.min(x.amount, y.amount)) + def maxOf(x: Satoshi, y: Satoshi) = Satoshi(Math.max(x.amount, y.amount)) + def minOf(x: Satoshi, y: Satoshi) = Satoshi(Math.min(x.amount, y.amount)) + } \ No newline at end of file diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Auditor.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Auditor.scala index 6136b928e4..0c79bd663c 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Auditor.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Auditor.scala @@ -18,12 +18,10 @@ package fr.acinq.eclair.payment import akka.actor.{Actor, ActorLogging, Props} import fr.acinq.bitcoin.ByteVector32 -import fr.acinq.eclair.NodeParams -import fr.acinq.eclair.channel.Channel.{LocalError, RemoteError} +import fr.acinq.eclair.{MilliSatoshi, NodeParams} import fr.acinq.eclair.channel.Helpers.Closing.{LocalClose, MutualClose, RecoveryClose, RemoteClose, RevokedClose} import fr.acinq.eclair.channel._ import fr.acinq.eclair.db.{AuditDb, ChannelLifecycleEvent} - import scala.concurrent.ExecutionContext import scala.concurrent.duration._ @@ -57,7 +55,7 @@ class Auditor(nodeParams: NodeParams) extends Actor with ActorLogging { case e: ChannelStateChanged => e match { case ChannelStateChanged(_, _, remoteNodeId, WAIT_FOR_FUNDING_LOCKED, NORMAL, d: DATA_NORMAL) => - db.add(ChannelLifecycleEvent(d.channelId, remoteNodeId, d.commitments.commitInput.txOut.amount.toLong, d.commitments.localParams.isFunder, !d.commitments.announceChannel, "created")) + db.add(ChannelLifecycleEvent(d.channelId, remoteNodeId, d.commitments.commitInput.txOut.amount, d.commitments.localParams.isFunder, !d.commitments.announceChannel, "created")) case _ => () } @@ -69,7 +67,7 @@ class Auditor(nodeParams: NodeParams) extends Actor with ActorLogging { case RecoveryClose => "recovery" case RevokedClose => "revoked" } - db.add(ChannelLifecycleEvent(e.channelId, e.commitments.remoteParams.nodeId, e.commitments.commitInput.txOut.amount.toLong, e.commitments.localParams.isFunder, !e.commitments.announceChannel, event)) + db.add(ChannelLifecycleEvent(e.channelId, e.commitments.remoteParams.nodeId, e.commitments.commitInput.txOut.amount, e.commitments.localParams.isFunder, !e.commitments.announceChannel, event)) } @@ -109,11 +107,11 @@ class BalanceEventThrottler(db: AuditDb) extends Actor with ActorLogging { case ProcessEvent(channelId) => pending.get(channelId) match { case Some(BalanceUpdate(first, last)) => - if (first.commitments.remoteCommit.spec.toRemoteMsat == last.localBalanceMsat) { + if (first.commitments.remoteCommit.spec.toRemote == last.localBalance) { // we don't log anything if the balance didn't change (e.g. it was a probe payment) log.info(s"ignoring balance event for channelId=$channelId (changed was discarded)") } else { - log.info(s"processing balance event for channelId=$channelId balance=${first.localBalanceMsat}->${last.localBalanceMsat}") + log.info(s"processing balance event for channelId=$channelId balance=${first.localBalance}->${last.localBalance}") // we log the last event, which contains the most up to date balance db.add(last) context.become(run(pending - channelId)) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Autoprobe.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Autoprobe.scala index 94e33deb94..71396e096e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Autoprobe.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Autoprobe.scala @@ -21,8 +21,8 @@ import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.eclair.crypto.Sphinx.DecryptedFailurePacket import fr.acinq.eclair.payment.PaymentLifecycle.{PaymentFailed, PaymentResult, RemoteFailure, SendPayment} import fr.acinq.eclair.router.{Announcements, Data} -import fr.acinq.eclair.wire.{IncorrectOrUnknownPaymentDetails} -import fr.acinq.eclair.{NodeParams, randomBytes32, secureRandom} +import fr.acinq.eclair.wire.IncorrectOrUnknownPaymentDetails +import fr.acinq.eclair.{MilliSatoshi, NodeParams, randomBytes32, secureRandom} import scala.concurrent.duration._ @@ -83,7 +83,7 @@ object Autoprobe { val PROBING_INTERVAL = 20 seconds - val PAYMENT_AMOUNT_MSAT = 100 * 1000 // this is below dust_limit so there won't be an output in the commitment tx + val PAYMENT_AMOUNT_MSAT = MilliSatoshi(100 * 1000) // this is below dust_limit so there won't be an output in the commitment tx object TickProbe diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/LocalPaymentHandler.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/LocalPaymentHandler.scala index 1070f3ef37..d936a1474a 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/LocalPaymentHandler.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/LocalPaymentHandler.scala @@ -17,7 +17,7 @@ package fr.acinq.eclair.payment import akka.actor.{Actor, ActorLogging, Props, Status} -import fr.acinq.bitcoin.{Crypto, MilliSatoshi} +import fr.acinq.bitcoin.{Crypto} 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 @@ -69,18 +69,18 @@ class LocalPaymentHandler(nodeParams: NodeParams) extends Actor with ActorLoggin sender ! CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(htlc.amountMsat)), commit = true) case _ if htlc.cltvExpiry < minFinalExpiry => sender ! CMD_FAIL_HTLC(htlc.id, Right(FinalExpiryTooSoon), commit = true) - case Some(amount) if MilliSatoshi(htlc.amountMsat) < amount => - log.warning(s"received payment with amount too small for paymentHash=${htlc.paymentHash} amountMsat=${htlc.amountMsat}") + 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)), commit = true) - case Some(amount) if MilliSatoshi(htlc.amountMsat) > amount * 2 => - log.warning(s"received payment with amount too large for paymentHash=${htlc.paymentHash} amountMsat=${htlc.amountMsat}") + 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)), commit = true) case _ => - log.info(s"received payment for paymentHash=${htlc.paymentHash} amountMsat=${htlc.amountMsat}") + 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(MilliSatoshi(htlc.amountMsat), htlc.paymentHash, htlc.channelId)) + context.system.eventStream.publish(PaymentReceived(htlc.amountMsat, htlc.paymentHash, htlc.channelId)) } case None => sender ! CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(htlc.amountMsat)), commit = true) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentEvents.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentEvents.scala index 5901dce810..7b0f1b55c2 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentEvents.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentEvents.scala @@ -17,7 +17,10 @@ package fr.acinq.eclair.payment import java.util.UUID -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi} + +import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.eclair.MilliSatoshi + import scala.compat.Platform /** diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentLifecycle.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentLifecycle.scala index 907a68a25c..74374ec9d5 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentLifecycle.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentLifecycle.scala @@ -20,7 +20,7 @@ import java.util.UUID import akka.actor.{ActorRef, FSM, Props, Status} import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi} +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.eclair._ import fr.acinq.eclair.channel.{AddHtlcFailed, CMD_ADD_HTLC, Channel, Register} import fr.acinq.eclair.crypto.{Sphinx, TransportHandler} @@ -47,14 +47,14 @@ class PaymentLifecycle(nodeParams: NodeParams, id: UUID, router: ActorRef, regis when(WAITING_FOR_REQUEST) { case Event(c: SendPaymentToRoute, WaitingForRequest) => - val send = SendPayment(c.amountMsat, c.paymentHash, c.hops.last, finalCltvExpiry = c.finalCltvExpiry, maxAttempts = 1) - paymentsDb.addOutgoingPayment(OutgoingPayment(id, c.paymentHash, None, c.amountMsat, Platform.currentTime, None, OutgoingPaymentStatus.PENDING)) + val send = SendPayment(c.amount, c.paymentHash, c.hops.last, finalCltvExpiry = c.finalCltvExpiry, maxAttempts = 1) + paymentsDb.addOutgoingPayment(OutgoingPayment(id, c.paymentHash, None, c.amount, Platform.currentTime, None, OutgoingPaymentStatus.PENDING)) router ! FinalizeRoute(c.hops) goto(WAITING_FOR_ROUTE) using WaitingForRoute(sender, send, failures = Nil) case Event(c: SendPayment, WaitingForRequest) => - router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amountMsat, c.assistedRoutes, routeParams = c.routeParams) - paymentsDb.addOutgoingPayment(OutgoingPayment(id, c.paymentHash, None, c.amountMsat, Platform.currentTime, None, OutgoingPaymentStatus.PENDING)) + router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amount, c.assistedRoutes, routeParams = c.routeParams) + paymentsDb.addOutgoingPayment(OutgoingPayment(id, c.paymentHash, None, c.amount, Platform.currentTime, None, OutgoingPaymentStatus.PENDING)) goto(WAITING_FOR_ROUTE) using WaitingForRoute(sender, c, failures = Nil) } @@ -65,7 +65,7 @@ class PaymentLifecycle(nodeParams: NodeParams, id: UUID, router: ActorRef, regis // we add one block in order to not have our htlc fail when a new block has just been found val finalExpiry = Globals.blockCount.get().toInt + c.finalCltvExpiry.toInt + 1 - val (cmd, sharedSecrets) = buildCommand(id, c.amountMsat, finalExpiry, c.paymentHash, hops) + val (cmd, sharedSecrets) = buildCommand(id,c.amount, finalExpiry, c.paymentHash, hops) register ! Register.ForwardShortId(firstHop.lastUpdate.shortChannelId, cmd) goto(WAITING_FOR_PAYMENT_COMPLETE) using WaitingForComplete(s, c, cmd, failures, sharedSecrets, ignoreNodes, ignoreChannels, hops) @@ -80,8 +80,8 @@ class PaymentLifecycle(nodeParams: NodeParams, id: UUID, router: ActorRef, regis case Event(fulfill: UpdateFulfillHtlc, WaitingForComplete(s, c, cmd, _, _, _, _, hops)) => paymentsDb.updateOutgoingPayment(id, OutgoingPaymentStatus.SUCCEEDED, preimage = Some(fulfill.paymentPreimage)) - reply(s, PaymentSucceeded(id, cmd.amountMsat, c.paymentHash, fulfill.paymentPreimage, hops)) - context.system.eventStream.publish(PaymentSent(id, MilliSatoshi(c.amountMsat), MilliSatoshi(cmd.amountMsat - c.amountMsat), cmd.paymentHash, fulfill.paymentPreimage, fulfill.channelId)) + reply(s, PaymentSucceeded(id, cmd.amount, c.paymentHash, fulfill.paymentPreimage, hops)) + context.system.eventStream.publish(PaymentSent(id, c.amount, cmd.amount - c.amount, cmd.paymentHash, fulfill.paymentPreimage, fulfill.channelId)) stop(FSM.Normal) case Event(fail: UpdateFailHtlc, WaitingForComplete(s, c, _, failures, sharedSecrets, ignoreNodes, ignoreChannels, hops)) => @@ -111,12 +111,12 @@ class PaymentLifecycle(nodeParams: NodeParams, id: UUID, router: ActorRef, regis // in that case we don't know which node is sending garbage, let's try to blacklist all nodes except the one we are directly connected to and the destination node val blacklist = hops.map(_.nextNodeId).drop(1).dropRight(1) log.warning(s"blacklisting intermediate nodes=${blacklist.mkString(",")}") - router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amountMsat, c.assistedRoutes, ignoreNodes ++ blacklist, ignoreChannels, c.routeParams) + router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amount, c.assistedRoutes, ignoreNodes ++ blacklist, ignoreChannels, c.routeParams) goto(WAITING_FOR_ROUTE) using WaitingForRoute(s, c, failures :+ UnreadableRemoteFailure(hops)) case Success(e@Sphinx.DecryptedFailurePacket(nodeId, failureMessage: Node)) => log.info(s"received 'Node' type error message from nodeId=$nodeId, trying to route around it (failure=$failureMessage)") // let's try to route around this node - router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amountMsat, c.assistedRoutes, ignoreNodes + nodeId, ignoreChannels, c.routeParams) + router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amount, c.assistedRoutes, ignoreNodes + nodeId, ignoreChannels, c.routeParams) goto(WAITING_FOR_ROUTE) using WaitingForRoute(s, c, failures :+ RemoteFailure(hops, e)) case Success(e@Sphinx.DecryptedFailurePacket(nodeId, failureMessage: Update)) => log.info(s"received 'Update' type error message from nodeId=$nodeId, retrying payment (failure=$failureMessage)") @@ -144,18 +144,18 @@ class PaymentLifecycle(nodeParams: NodeParams, id: UUID, router: ActorRef, regis // in any case, we forward the update to the router router ! failureMessage.update // let's try again, router will have updated its state - router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amountMsat, c.assistedRoutes, ignoreNodes, ignoreChannels, c.routeParams) + router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amount, c.assistedRoutes, ignoreNodes, ignoreChannels, c.routeParams) } else { // this node is fishy, it gave us a bad sig!! let's filter it out log.warning(s"got bad signature from node=$nodeId update=${failureMessage.update}") - router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amountMsat, c.assistedRoutes, ignoreNodes + nodeId, ignoreChannels, c.routeParams) + router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amount, c.assistedRoutes, ignoreNodes + nodeId, ignoreChannels, c.routeParams) } goto(WAITING_FOR_ROUTE) using WaitingForRoute(s, c, failures :+ RemoteFailure(hops, e)) case Success(e@Sphinx.DecryptedFailurePacket(nodeId, failureMessage)) => log.info(s"received an error message from nodeId=$nodeId, trying to use a different channel (failure=$failureMessage)") // let's try again without the channel outgoing from nodeId val faultyChannel = hops.find(_.nodeId == nodeId).map(hop => ChannelDesc(hop.lastUpdate.shortChannelId, hop.nodeId, hop.nextNodeId)) - router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amountMsat, c.assistedRoutes, ignoreNodes, ignoreChannels ++ faultyChannel.toSet, c.routeParams) + router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amount, c.assistedRoutes, ignoreNodes, ignoreChannels ++ faultyChannel.toSet, c.routeParams) goto(WAITING_FOR_ROUTE) using WaitingForRoute(s, c, failures :+ RemoteFailure(hops, e)) } @@ -175,7 +175,7 @@ class PaymentLifecycle(nodeParams: NodeParams, id: UUID, router: ActorRef, regis } else { log.info(s"received an error message from local, trying to use a different channel (failure=${t.getMessage})") val faultyChannel = ChannelDesc(hops.head.lastUpdate.shortChannelId, hops.head.nodeId, hops.head.nextNodeId) - router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amountMsat, c.assistedRoutes, ignoreNodes, ignoreChannels + faultyChannel, c.routeParams) + router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amount, c.assistedRoutes, ignoreNodes, ignoreChannels + faultyChannel, c.routeParams) goto(WAITING_FOR_ROUTE) using WaitingForRoute(s, c, failures :+ LocalFailure(t)) } @@ -198,21 +198,21 @@ object PaymentLifecycle { def props(nodeParams: NodeParams, id: UUID, router: ActorRef, register: ActorRef) = Props(classOf[PaymentLifecycle], nodeParams, id, router, register) // @formatter:off - case class ReceivePayment(amountMsat_opt: Option[MilliSatoshi], description: String, expirySeconds_opt: Option[Long] = None, extraHops: List[List[ExtraHop]] = Nil, fallbackAddress: Option[String] = None, paymentPreimage: Option[ByteVector32] = None) + case class ReceivePayment(amount_opt: Option[MilliSatoshi], description: String, expirySeconds_opt: Option[Long] = None, extraHops: List[List[ExtraHop]] = Nil, fallbackAddress: Option[String] = None, paymentPreimage: Option[ByteVector32] = None) sealed trait GenericSendPayment - case class SendPaymentToRoute(amountMsat: Long, paymentHash: ByteVector32, hops: Seq[PublicKey], finalCltvExpiry: Long = Channel.MIN_CLTV_EXPIRY) extends GenericSendPayment - case class SendPayment(amountMsat: Long, + case class SendPaymentToRoute(amount: MilliSatoshi, paymentHash: ByteVector32, hops: Seq[PublicKey], finalCltvExpiry: Long = Channel.MIN_CLTV_EXPIRY) extends GenericSendPayment + case class SendPayment(amount: MilliSatoshi, paymentHash: ByteVector32, targetNodeId: PublicKey, assistedRoutes: Seq[Seq[ExtraHop]] = Nil, finalCltvExpiry: Long = Channel.MIN_CLTV_EXPIRY, maxAttempts: Int, routeParams: Option[RouteParams] = None) extends GenericSendPayment { - require(amountMsat > 0, s"amountMsat must be > 0") + require(amount > MilliSatoshi(0), s"amountMsat must be > 0") } sealed trait PaymentResult - case class PaymentSucceeded(id: UUID, amountMsat: Long, paymentHash: ByteVector32, paymentPreimage: ByteVector32, route: Seq[Hop]) extends PaymentResult // note: the amount includes fees + case class PaymentSucceeded(id: UUID, amount: MilliSatoshi, paymentHash: ByteVector32, paymentPreimage: ByteVector32, route: Seq[Hop]) extends PaymentResult // note: the amount includes fees sealed trait PaymentFailure case class LocalFailure(t: Throwable) extends PaymentFailure case class RemoteFailure(route: Seq[Hop], e: Sphinx.DecryptedFailurePacket) extends PaymentFailure @@ -246,7 +246,7 @@ object PaymentLifecycle { /** * - * @param finalAmountMsat the final htlc amount in millisatoshis + * @param finalAmount the final htlc amount in millisatoshis * @param finalExpiry the final htlc expiry in number of blocks * @param hops the hops as computed by the router + extra routes from payment request * @return a (firstAmountMsat, firstExpiry, payloads) tuple where: @@ -254,19 +254,19 @@ object PaymentLifecycle { * - firstExpiry is the cltv expiry for the first htlc in the route * - a sequence of payloads that will be used to build the onion */ - def buildPayloads(finalAmountMsat: Long, finalExpiry: Long, hops: Seq[Hop]): (Long, Long, Seq[PerHopPayload]) = - hops.reverse.foldLeft((finalAmountMsat, finalExpiry, PerHopPayload(ShortChannelId(0L), finalAmountMsat, finalExpiry) :: Nil)) { + def buildPayloads(finalAmount: MilliSatoshi, finalExpiry: Long, hops: Seq[Hop]): (MilliSatoshi, Long, Seq[PerHopPayload]) = + hops.reverse.foldLeft((finalAmount, finalExpiry, PerHopPayload(ShortChannelId(0L), finalAmount, finalExpiry) :: Nil)) { case ((msat, expiry, payloads), hop) => val nextFee = nodeFee(hop.lastUpdate.feeBaseMsat, hop.lastUpdate.feeProportionalMillionths, msat) (msat + nextFee, expiry + hop.lastUpdate.cltvExpiryDelta, PerHopPayload(hop.lastUpdate.shortChannelId, msat, expiry) +: payloads) } - def buildCommand(id: UUID, finalAmountMsat: Long, finalExpiry: Long, paymentHash: ByteVector32, hops: Seq[Hop]): (CMD_ADD_HTLC, Seq[(ByteVector32, PublicKey)]) = { - val (firstAmountMsat, firstExpiry, payloads) = buildPayloads(finalAmountMsat, finalExpiry, hops.drop(1)) + def buildCommand(id: UUID, finalAmount: MilliSatoshi, finalExpiry: Long, paymentHash: ByteVector32, hops: Seq[Hop]): (CMD_ADD_HTLC, Seq[(ByteVector32, PublicKey)]) = { + val (firstAmount, firstExpiry, payloads) = buildPayloads(finalAmount, finalExpiry, hops.drop(1)) val nodes = hops.map(_.nextNodeId) // BOLT 2 requires that associatedData == paymentHash val onion = buildOnion(nodes, payloads, paymentHash) - CMD_ADD_HTLC(firstAmountMsat, paymentHash, firstExpiry, onion.packet, upstream = Left(id), commit = true) -> onion.sharedSecrets + CMD_ADD_HTLC(firstAmount, paymentHash, firstExpiry, onion.packet, upstream = Left(id), commit = true) -> onion.sharedSecrets } /** diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentRequest.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentRequest.scala index b5c6da04f9..fc6ea0ca3e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentRequest.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentRequest.scala @@ -16,13 +16,14 @@ package fr.acinq.eclair.payment +import fr.acinq.bitcoin.{Base58, Base58Check, Bech32, Block, ByteVector32, ByteVector64, Crypto} import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin.{MilliSatoshi, _} -import fr.acinq.eclair.ShortChannelId +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId} import fr.acinq.eclair.payment.PaymentRequest._ import scodec.Codec import scodec.bits.{BitVector, ByteOrdering, ByteVector} import scodec.codecs.{list, ubyte} + import scala.concurrent.duration._ import scala.compat.Platform import scala.util.Try diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Relayer.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Relayer.scala index d5d6b9f17e..7112bdd0ba 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Relayer.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Relayer.scala @@ -21,14 +21,14 @@ import java.util.UUID import akka.actor.{Actor, ActorLogging, ActorRef, Props, Status} import akka.event.LoggingAdapter import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi} +import fr.acinq.bitcoin.ByteVector32 import fr.acinq.eclair.channel._ import fr.acinq.eclair.crypto.Sphinx import fr.acinq.eclair.db.OutgoingPaymentStatus import fr.acinq.eclair.payment.PaymentLifecycle.{PaymentFailed, PaymentSucceeded} import fr.acinq.eclair.router.Announcements import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{NodeParams, ShortChannelId, nodeFee} +import fr.acinq.eclair.{MilliSatoshi, NodeParams, ShortChannelId, nodeFee} import grizzled.slf4j.Logging import scodec.{Attempt, DecodeResult} @@ -38,7 +38,7 @@ import scala.collection.mutable sealed trait Origin case class Local(id: UUID, sender: Option[ActorRef]) extends Origin // we don't persist reference to local actors -case class Relayed(originChannelId: ByteVector32, originHtlcId: Long, amountMsatIn: Long, amountMsatOut: Long) extends Origin +case class Relayed(originChannelId: ByteVector32, originHtlcId: Long, amountIn: MilliSatoshi, amountOut: MilliSatoshi) extends Origin sealed trait ForwardMessage case class ForwardAdd(add: UpdateAddHtlc, previousFailures: Seq[AddHtlcFailed] = Seq.empty) extends ForwardMessage @@ -47,7 +47,7 @@ case class ForwardFail(fail: UpdateFailHtlc, to: Origin, htlc: UpdateAddHtlc) ex case class ForwardFailMalformed(fail: UpdateFailMalformedHtlc, to: Origin, htlc: UpdateAddHtlc) extends ForwardMessage case object GetUsableBalances -case class UsableBalances(remoteNodeId: PublicKey, shortChannelId: ShortChannelId, canSendMsat: Long, canReceiveMsat: Long, isPublic: Boolean) +case class UsableBalances(remoteNodeId: PublicKey, shortChannelId: ShortChannelId, canSend: MilliSatoshi, canReceive: MilliSatoshi, isPublic: Boolean) // @formatter:on @@ -77,8 +77,8 @@ class Relayer(nodeParams: NodeParams, register: ActorRef, paymentHandler: ActorR .map(o => UsableBalances( remoteNodeId = o.nextNodeId, shortChannelId = o.channelUpdate.shortChannelId, - canSendMsat = o.commitments.availableBalanceForSendMsat, - canReceiveMsat = o.commitments.availableBalanceForReceiveMsat, + canSend = o.commitments.availableBalanceForSend, + canReceive = o.commitments.availableBalanceForReceive, isPublic = o.commitments.announceChannel)) case LocalChannelUpdate(_, channelId, shortChannelId, remoteNodeId, _, channelUpdate, commitments) => @@ -158,17 +158,17 @@ class Relayer(nodeParams: NodeParams, register: ActorRef, paymentHandler: ActorR to match { case Local(id, None) => val feesPaid = MilliSatoshi(0) - context.system.eventStream.publish(PaymentSent(id, MilliSatoshi(add.amountMsat), feesPaid, add.paymentHash, fulfill.paymentPreimage, fulfill.channelId)) + context.system.eventStream.publish(PaymentSent(id, add.amountMsat, feesPaid, add.paymentHash, fulfill.paymentPreimage, fulfill.channelId)) // we sent the payment, but we probably restarted and the reference to the original sender was lost, // we publish the failure on the event stream and update the status in paymentDb nodeParams.db.payments.updateOutgoingPayment(id, OutgoingPaymentStatus.SUCCEEDED, Some(fulfill.paymentPreimage)) context.system.eventStream.publish(PaymentSucceeded(id, add.amountMsat, add.paymentHash, fulfill.paymentPreimage, Nil)) // case Local(_, Some(sender)) => sender ! fulfill - case Relayed(originChannelId, originHtlcId, amountMsatIn, amountMsatOut) => + case Relayed(originChannelId, originHtlcId, amountIn, amountOut) => val cmd = CMD_FULFILL_HTLC(originHtlcId, fulfill.paymentPreimage, commit = true) commandBuffer ! CommandBuffer.CommandSend(originChannelId, originHtlcId, cmd) - context.system.eventStream.publish(PaymentRelayed(MilliSatoshi(amountMsatIn), MilliSatoshi(amountMsatOut), add.paymentHash, fromChannelId = originChannelId, toChannelId = fulfill.channelId)) + context.system.eventStream.publish(PaymentRelayed(amountIn, amountOut, add.paymentHash, fromChannelId = originChannelId, toChannelId = fulfill.channelId)) } case ForwardFail(fail, to, add) => @@ -215,7 +215,7 @@ object Relayer extends Logging { sealed trait NextPayload case class FinalPayload(add: UpdateAddHtlc, payload: PerHopPayload) extends NextPayload case class RelayPayload(add: UpdateAddHtlc, payload: PerHopPayload, nextPacket: OnionRoutingPacket) extends NextPayload { - val relayFeeMsat: Long = add.amountMsat - payload.amtToForward + val relayFeeMsat: MilliSatoshi = add.amountMsat - payload.amtToForward val expiryDelta: Long = add.cltvExpiry - payload.outgoingCltvValue } // @formatter:on @@ -330,10 +330,10 @@ object Relayer extends Logging { val channelInfo_opt = channelUpdates.get(shortChannelId) val channelUpdate_opt = channelInfo_opt.map(_.channelUpdate) val relayResult = relayOrFail(relayPayload, channelUpdate_opt) - log.debug(s"candidate channel for htlc #${add.id} paymentHash=${add.paymentHash}: shortChannelId={} balanceMsat={} channelUpdate={} relayResult={}", shortChannelId, channelInfo_opt.map(_.commitments.availableBalanceForSendMsat).getOrElse(""), channelUpdate_opt.getOrElse(""), relayResult) + log.debug(s"candidate channel for htlc #${add.id} paymentHash=${add.paymentHash}: shortChannelId={} balanceMsat={} channelUpdate={} relayResult={}", shortChannelId, channelInfo_opt.map(_.commitments.availableBalanceForSend).getOrElse(""), channelUpdate_opt.getOrElse(""), relayResult) (shortChannelId, channelInfo_opt, relayResult) } - .collect { case (shortChannelId, Some(channelInfo), _: RelaySuccess) => (shortChannelId, channelInfo.commitments.availableBalanceForSendMsat) } + .collect { case (shortChannelId, Some(channelInfo), _: RelaySuccess) => (shortChannelId, channelInfo.commitments.availableBalanceForSend) } .filter(_._2 > relayPayload.payload.amtToForward) // we only keep channels that have enough balance to handle this payment .toList // needed for ordering .sortBy(_._2) // we want to use the channel with the lowest available balance that can process the payment diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/router/Announcements.scala b/eclair-core/src/main/scala/fr/acinq/eclair/router/Announcements.scala index 1e76f48b7b..abbfd61d4e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/router/Announcements.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/router/Announcements.scala @@ -19,7 +19,7 @@ package fr.acinq.eclair.router import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey, sha256, verifySignature} import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Crypto, LexicographicalOrdering} import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{Features, ShortChannelId, serializationResult} +import fr.acinq.eclair.{Features, MilliSatoshi, ShortChannelId, serializationResult} import scodec.bits.{BitVector, ByteVector} import shapeless.HNil @@ -37,7 +37,7 @@ object Announcements { def nodeAnnouncementWitnessEncode(timestamp: Long, nodeId: PublicKey, rgbColor: Color, alias: String, features: ByteVector, addresses: List[NodeAddress], unknownFields: ByteVector): ByteVector = sha256(sha256(serializationResult(LightningMessageCodecs.nodeAnnouncementWitnessCodec.encode(features :: timestamp :: nodeId :: rgbColor :: alias :: addresses :: unknownFields :: HNil)))) - def channelUpdateWitnessEncode(chainHash: ByteVector32, shortChannelId: ShortChannelId, timestamp: Long, messageFlags: Byte, channelFlags: Byte, cltvExpiryDelta: Int, htlcMinimumMsat: Long, feeBaseMsat: Long, feeProportionalMillionths: Long, htlcMaximumMsat: Option[Long], unknownFields: ByteVector): ByteVector = + def channelUpdateWitnessEncode(chainHash: ByteVector32, shortChannelId: ShortChannelId, timestamp: Long, messageFlags: Byte, channelFlags: Byte, cltvExpiryDelta: Int, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, htlcMaximumMsat: Option[MilliSatoshi], unknownFields: ByteVector): ByteVector = sha256(sha256(serializationResult(LightningMessageCodecs.channelUpdateWitnessCodec.encode(chainHash :: shortChannelId :: timestamp :: messageFlags :: channelFlags :: cltvExpiryDelta :: htlcMinimumMsat :: feeBaseMsat :: feeProportionalMillionths :: htlcMaximumMsat :: unknownFields :: HNil)))) def signChannelAnnouncement(chainHash: ByteVector32, shortChannelId: ShortChannelId, localNodeSecret: PrivateKey, remoteNodeId: PublicKey, localFundingPrivKey: PrivateKey, remoteFundingKey: PublicKey, features: ByteVector): (ByteVector64, ByteVector64) = { @@ -128,7 +128,7 @@ object Announcements { def makeChannelFlags(isNode1: Boolean, enable: Boolean): Byte = BitVector.bits(!enable :: !isNode1 :: Nil).padLeft(8).toByte() - def makeChannelUpdate(chainHash: ByteVector32, nodeSecret: PrivateKey, remoteNodeId: PublicKey, shortChannelId: ShortChannelId, cltvExpiryDelta: Int, htlcMinimumMsat: Long, feeBaseMsat: Long, feeProportionalMillionths: Long, htlcMaximumMsat: Long, enable: Boolean = true, timestamp: Long = Platform.currentTime.milliseconds.toSeconds): ChannelUpdate = { + def makeChannelUpdate(chainHash: ByteVector32, nodeSecret: PrivateKey, remoteNodeId: PublicKey, shortChannelId: ShortChannelId, cltvExpiryDelta: Int, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, htlcMaximumMsat: MilliSatoshi, enable: Boolean = true, timestamp: Long = Platform.currentTime.milliseconds.toSeconds): ChannelUpdate = { val messageFlags = makeMessageFlags(hasOptionChannelHtlcMax = true) // NB: we always support option_channel_htlc_max val channelFlags = makeChannelFlags(isNode1 = isNode1(nodeSecret.publicKey, remoteNodeId), enable = enable) val htlcMaximumMsatOpt = Some(htlcMaximumMsat) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/router/Graph.scala b/eclair-core/src/main/scala/fr/acinq/eclair/router/Graph.scala index 178036a3ae..27e92a3238 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/router/Graph.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/router/Graph.scala @@ -29,7 +29,7 @@ object Graph { // @formatter:off // A compound weight for an edge, weight is obtained with (cost X factor),'cost' contains the actual amount+fees in millisatoshi, 'cltvCumulative' the total CLTV necessary to reach this edge - case class RichWeight(cost: Long, length: Int, cltv: Int, weight: Double) extends Ordered[RichWeight] { + case class RichWeight(cost: MilliSatoshi, length: Int, cltv: Int, weight: Double) extends Ordered[RichWeight] { override def compare(that: RichWeight): Int = this.weight.compareTo(that.weight) } case class WeightRatios(cltvDeltaFactor: Double, ageFactor: Double, capacityFactor: Double) { @@ -62,7 +62,7 @@ object Graph { * @param graph * @param sourceNode * @param targetNode - * @param amountMsat + * @param amount * @param pathsToFind * @param wr an object containing the ratios used to 'weight' edges when searching for the shortest path * @param currentBlockHeight the height of the chain tip (latest block) @@ -72,7 +72,7 @@ object Graph { def yenKshortestPaths(graph: DirectedGraph, sourceNode: PublicKey, targetNode: PublicKey, - amountMsat: Long, + amount: MilliSatoshi, ignoredEdges: Set[ChannelDesc], extraEdges: Set[GraphEdge], pathsToFind: Int, @@ -88,9 +88,9 @@ object Graph { val candidates = new mutable.PriorityQueue[WeightedPath] // find the shortest path, k = 0 - val initialWeight = RichWeight(cost = amountMsat, 0, 0, 0) + val initialWeight = RichWeight(cost = amount, 0, 0, 0) val shortestPath = dijkstraShortestPath(graph, sourceNode, targetNode, ignoredEdges, extraEdges, initialWeight, boundaries, currentBlockHeight, wr) - shortestPaths += WeightedPath(shortestPath, pathWeight(shortestPath, amountMsat, isPartial = false, currentBlockHeight, wr)) + shortestPaths += WeightedPath(shortestPath, pathWeight(shortestPath, amount, isPartial = false, currentBlockHeight, wr)) // avoid returning a list with an empty path if (shortestPath.isEmpty) return Seq.empty @@ -110,7 +110,7 @@ object Graph { // select the sub-path from the source to the spur node of the k-th previous shortest path val rootPathEdges = if (i == 0) prevShortestPath.head :: Nil else prevShortestPath.take(i) - val rootPathWeight = pathWeight(rootPathEdges, amountMsat, isPartial = true, currentBlockHeight, wr) + val rootPathWeight = pathWeight(rootPathEdges, amount, isPartial = true, currentBlockHeight, wr) // links to be removed that are part of the previous shortest path and which share the same root path val edgesToIgnore = shortestPaths.flatMap { weightedPath => @@ -136,7 +136,7 @@ object Graph { case false => rootPathEdges ++ spurPath } - val candidatePath = WeightedPath(totalPath, pathWeight(totalPath, amountMsat, isPartial = false, currentBlockHeight, wr)) + val candidatePath = WeightedPath(totalPath, pathWeight(totalPath, amount, isPartial = false, currentBlockHeight, wr)) if (boundaries(candidatePath.weight) && !shortestPaths.contains(candidatePath) && !candidates.exists(_ == candidatePath)) { candidates.enqueue(candidatePath) @@ -237,7 +237,7 @@ object Graph { // we call containsKey first because "getOrDefault" is not available in JDK7 val neighborCost = weight.containsKey(neighbor) match { - case false => RichWeight(Long.MaxValue, Int.MaxValue, Int.MaxValue, Double.MaxValue) + case false => RichWeight(MilliSatoshi(Long.MaxValue), Int.MaxValue, Int.MaxValue, Double.MaxValue) case true => weight.get(neighbor) } @@ -281,7 +281,7 @@ object Graph { private def edgeWeight(edge: GraphEdge, prev: RichWeight, isNeighborTarget: Boolean, currentBlockHeight: Long, weightRatios: Option[WeightRatios]): RichWeight = weightRatios match { case None => val edgeCost = if (isNeighborTarget) prev.cost else edgeFeeCost(edge, prev.cost) - RichWeight(cost = edgeCost, length = prev.length + 1, cltv = prev.cltv + edge.update.cltvExpiryDelta, weight = edgeCost) + RichWeight(cost = edgeCost, length = prev.length + 1, cltv = prev.cltv + edge.update.cltvExpiryDelta, weight = edgeCost.toLong) case Some(wr) => import RoutingHeuristics._ @@ -291,8 +291,8 @@ object Graph { val ageFactor = normalize(channelBlockHeight, min = currentBlockHeight - BLOCK_TIME_TWO_MONTHS, max = currentBlockHeight) // Every edge is weighted by channel capacity, larger channels add less weight - val edgeMaxCapacity = edge.update.htlcMaximumMsat.getOrElse(CAPACITY_CHANNEL_LOW_MSAT) - val capFactor = 1 - normalize(edgeMaxCapacity, CAPACITY_CHANNEL_LOW_MSAT, CAPACITY_CHANNEL_HIGH_MSAT) + val edgeMaxCapacity = edge.update.htlcMaximumMsat.getOrElse(CAPACITY_CHANNEL_LOW) + val capFactor = 1 - normalize(edgeMaxCapacity.toLong, CAPACITY_CHANNEL_LOW.toLong, CAPACITY_CHANNEL_HIGH.toLong) // Every edge is weighted by its clvt-delta value, normalized val channelCltvDelta = edge.update.cltvExpiryDelta @@ -303,7 +303,7 @@ object Graph { // NB we're guaranteed to have weightRatios and factors > 0 val factor = (cltvFactor * wr.cltvDeltaFactor) + (ageFactor * wr.ageFactor) + (capFactor * wr.capacityFactor) - val edgeWeight = if (isNeighborTarget) prev.weight else prev.weight + edgeCost * factor + val edgeWeight = if (isNeighborTarget) prev.weight else prev.weight + edgeCost.toLong * factor RichWeight(cost = edgeCost, length = prev.length + 1, cltv = prev.cltv + channelCltvDelta, weight = edgeWeight) } @@ -317,17 +317,17 @@ object Graph { * @param amountWithFees the value that this edge will have to carry along * @return the new amount updated with the necessary fees for this edge */ - private def edgeFeeCost(edge: GraphEdge, amountWithFees: Long): Long = { - if(edgeHasZeroFee(edge)) amountWithFees + nodeFee(baseMsat = 1, proportional = 0, amountWithFees) + private def edgeFeeCost(edge: GraphEdge, amountWithFees: MilliSatoshi):MilliSatoshi = { + if(edgeHasZeroFee(edge)) amountWithFees + nodeFee(baseMsat = MilliSatoshi(1), proportional = 0, amountWithFees) else amountWithFees + nodeFee(edge.update.feeBaseMsat, edge.update.feeProportionalMillionths, amountWithFees) } private def edgeHasZeroFee(edge: GraphEdge): Boolean = { - edge.update.feeBaseMsat == 0 && edge.update.feeProportionalMillionths == 0 + edge.update.feeBaseMsat.toLong == 0 && edge.update.feeProportionalMillionths == 0 } // Calculates the total cost of a path (amount + fees), direct channels with the source will have a cost of 0 (pay no fees) - def pathWeight(path: Seq[GraphEdge], amountMsat: Long, isPartial: Boolean, currentBlockHeight: Long, wr: Option[WeightRatios]): RichWeight = { + def pathWeight(path: Seq[GraphEdge], amountMsat: MilliSatoshi, isPartial: Boolean, currentBlockHeight: Long, wr: Option[WeightRatios]): RichWeight = { path.drop(if (isPartial) 0 else 1).foldRight(RichWeight(amountMsat, 0, 0, 0)) { (edge, prev) => edgeWeight(edge, prev, false, currentBlockHeight, wr) } @@ -340,8 +340,8 @@ object Graph { val BLOCK_TIME_TWO_MONTHS = 8640 // Low/High bound for channel capacity - val CAPACITY_CHANNEL_LOW_MSAT = 1000 * 1000L // 1000 sat - val CAPACITY_CHANNEL_HIGH_MSAT = Channel.MAX_FUNDING_SATOSHIS * 1000L + val CAPACITY_CHANNEL_LOW = MilliSatoshi(1000 * 1000L) // 1000 sat + val CAPACITY_CHANNEL_HIGH = Channel.MAX_FUNDING.toMilliSatoshi // Low/High bound for CLTV channel value val CLTV_LOW = 9 diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala b/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala index 77b70124eb..6e969dbb1e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala @@ -19,7 +19,7 @@ package fr.acinq.eclair.router import akka.Done import akka.actor.{ActorRef, Props, Status} import akka.event.Logging.MDC -import fr.acinq.bitcoin.{ByteVector32, ByteVector64} +import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Satoshi} import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.bitcoin.Script.{pay2wsh, write} import fr.acinq.eclair._ @@ -47,7 +47,7 @@ import scala.util.{Random, Try} case class RouterConf(randomizeRouteSelection: Boolean, channelExcludeDuration: FiniteDuration, routerBroadcastInterval: FiniteDuration, - searchMaxFeeBaseSat: Long, + searchMaxFeeBase: Satoshi, searchMaxFeePct: Double, searchMaxRouteLength: Int, searchMaxCltv: Int, @@ -58,10 +58,10 @@ case class RouterConf(randomizeRouteSelection: Boolean, case class ChannelDesc(shortChannelId: ShortChannelId, a: PublicKey, b: PublicKey) case class Hop(nodeId: PublicKey, nextNodeId: PublicKey, lastUpdate: ChannelUpdate) -case class RouteParams(randomize: Boolean, maxFeeBaseMsat: Long, maxFeePct: Double, routeMaxLength: Int, routeMaxCltv: Int, ratios: Option[WeightRatios]) +case class RouteParams(randomize: Boolean, maxFeeBase: MilliSatoshi, maxFeePct: Double, routeMaxLength: Int, routeMaxCltv: Int, ratios: Option[WeightRatios]) case class RouteRequest(source: PublicKey, target: PublicKey, - amountMsat: Long, + amount: MilliSatoshi, assistedRoutes: Seq[Seq[ExtraHop]] = Nil, ignoreNodes: Set[PublicKey] = Set.empty, ignoreChannels: Set[ChannelDesc] = Set.empty, @@ -732,7 +732,7 @@ object Router { def toFakeUpdate(extraHop: ExtraHop): ChannelUpdate = // the `direction` bit in flags will not be accurate but it doesn't matter because it is not used // what matters is that the `disable` bit is 0 so that this update doesn't get filtered out - ChannelUpdate(signature = ByteVector64.Zeroes, chainHash = ByteVector32.Zeroes, extraHop.shortChannelId, Platform.currentTime.milliseconds.toSeconds, messageFlags = 0, channelFlags = 0, extraHop.cltvExpiryDelta, htlcMinimumMsat = 0L, extraHop.feeBaseMsat, extraHop.feeProportionalMillionths, None) + ChannelUpdate(signature = ByteVector64.Zeroes, chainHash = ByteVector32.Zeroes, extraHop.shortChannelId, Platform.currentTime.milliseconds.toSeconds, messageFlags = 0, channelFlags = 0, extraHop.cltvExpiryDelta, htlcMinimumMsat = MilliSatoshi(0), MilliSatoshi(extraHop.feeBaseMsat), extraHop.feeProportionalMillionths, None) def toFakeUpdates(extraRoute: Seq[ExtraHop], targetNodeId: PublicKey): Map[ChannelDesc, ChannelUpdate] = { // BOLT 11: "For each entry, the pubkey is the node ID of the start of the channel", and the last node is the destination @@ -827,7 +827,7 @@ object Router { def getDefaultRouteParams(routerConf: RouterConf) = RouteParams( randomize = routerConf.randomizeRouteSelection, - maxFeeBaseMsat = routerConf.searchMaxFeeBaseSat * 1000, // converting sat -> msat + maxFeeBase = routerConf.searchMaxFeeBase.toMilliSatoshi, maxFeePct = routerConf.searchMaxFeePct, routeMaxLength = routerConf.searchMaxRouteLength, routeMaxCltv = routerConf.searchMaxCltv, @@ -848,7 +848,7 @@ object Router { * @param g * @param localNodeId * @param targetNodeId - * @param amountMsat the amount that will be sent along this route + * @param amount the amount that will be sent along this route * @param numRoutes the number of shortest-paths to find * @param extraEdges a set of extra edges we want to CONSIDER during the search * @param ignoredEdges a set of extra edges we want to IGNORE during the search @@ -858,7 +858,7 @@ object Router { def findRoute(g: DirectedGraph, localNodeId: PublicKey, targetNodeId: PublicKey, - amountMsat: Long, + amount: MilliSatoshi, numRoutes: Int, extraEdges: Set[GraphEdge] = Set.empty, ignoredEdges: Set[ChannelDesc] = Set.empty, @@ -869,14 +869,14 @@ object Router { val currentBlockHeight = Globals.blockCount.get() val boundaries: RichWeight => Boolean = { weight => - ((weight.cost - amountMsat) < routeParams.maxFeeBaseMsat || (weight.cost - amountMsat) < (routeParams.maxFeePct * amountMsat)) && + ((weight.cost - amount) < routeParams.maxFeeBase || (weight.cost - amount) < amount * routeParams.maxFeePct.toLong) && weight.length <= routeParams.routeMaxLength && weight.length <= ROUTE_MAX_LENGTH && weight.cltv <= routeParams.routeMaxCltv } - val foundRoutes = Graph.yenKshortestPaths(g, localNodeId, targetNodeId, amountMsat, ignoredEdges, extraEdges, numRoutes, routeParams.ratios, currentBlockHeight, boundaries).toList match { + val foundRoutes = Graph.yenKshortestPaths(g, localNodeId, targetNodeId, amount, ignoredEdges, 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, amountMsat, numRoutes, extraEdges, ignoredEdges, routeParams.copy(routeMaxLength = ROUTE_MAX_LENGTH, routeMaxCltv = DEFAULT_ROUTE_MAX_CLTV)) + return findRoute(g, localNodeId, targetNodeId, amount, numRoutes, extraEdges, ignoredEdges, routeParams.copy(routeMaxLength = ROUTE_MAX_LENGTH, routeMaxCltv = DEFAULT_ROUTE_MAX_CLTV)) case Nil => throw RouteNotFound case routes => routes.find(_.path.size == 1) match { case Some(directRoute) => directRoute :: Nil diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/transactions/CommitmentSpec.scala b/eclair-core/src/main/scala/fr/acinq/eclair/transactions/CommitmentSpec.scala index 3448ecfe99..d4b201202d 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/transactions/CommitmentSpec.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/transactions/CommitmentSpec.scala @@ -16,6 +16,7 @@ package fr.acinq.eclair.transactions +import fr.acinq.eclair.MilliSatoshi import fr.acinq.eclair.wire._ /** @@ -30,8 +31,8 @@ case object OUT extends Direction { def opposite = IN } case class DirectedHtlc(direction: Direction, add: UpdateAddHtlc) -final case class CommitmentSpec(htlcs: Set[DirectedHtlc], feeratePerKw: Long, toLocalMsat: Long, toRemoteMsat: Long) { - val totalFunds = toLocalMsat + toRemoteMsat + htlcs.toSeq.map(_.add.amountMsat).sum +final case class CommitmentSpec(htlcs: Set[DirectedHtlc], feeratePerKw: Long, toLocal: MilliSatoshi, toRemote: MilliSatoshi) { + val totalFunds = toLocal + toRemote + htlcs.toSeq.map(_.add.amountMsat).sum } object CommitmentSpec { @@ -43,16 +44,16 @@ object CommitmentSpec { def addHtlc(spec: CommitmentSpec, direction: Direction, update: UpdateAddHtlc): CommitmentSpec = { val htlc = DirectedHtlc(direction, update) direction match { - case OUT => spec.copy(toLocalMsat = spec.toLocalMsat - htlc.add.amountMsat, htlcs = spec.htlcs + htlc) - case IN => spec.copy(toRemoteMsat = spec.toRemoteMsat - htlc.add.amountMsat, htlcs = spec.htlcs + htlc) + case OUT => spec.copy(toLocal = spec.toLocal - htlc.add.amountMsat, htlcs = spec.htlcs + htlc) + case IN => spec.copy(toRemote = spec.toRemote - htlc.add.amountMsat, htlcs = spec.htlcs + htlc) } } // OUT means we are sending an UpdateFulfillHtlc message which means that we are fulfilling an HTLC that they sent def fulfillHtlc(spec: CommitmentSpec, direction: Direction, htlcId: Long): CommitmentSpec = { spec.htlcs.find(htlc => htlc.direction != direction && htlc.add.id == htlcId) match { - case Some(htlc) if direction == OUT => spec.copy(toLocalMsat = spec.toLocalMsat + htlc.add.amountMsat, htlcs = spec.htlcs - htlc) - case Some(htlc) if direction == IN => spec.copy(toRemoteMsat = spec.toRemoteMsat + htlc.add.amountMsat, htlcs = spec.htlcs - htlc) + case Some(htlc) if direction == OUT => spec.copy(toLocal = spec.toLocal + htlc.add.amountMsat, htlcs = spec.htlcs - htlc) + case Some(htlc) if direction == IN => spec.copy(toRemote = spec.toRemote + htlc.add.amountMsat, htlcs = spec.htlcs - htlc) case None => throw new RuntimeException(s"cannot find htlc id=${htlcId}") } } @@ -60,8 +61,8 @@ object CommitmentSpec { // OUT means we are sending an UpdateFailHtlc message which means that we are failing an HTLC that they sent def failHtlc(spec: CommitmentSpec, direction: Direction, htlcId: Long): CommitmentSpec = { spec.htlcs.find(htlc => htlc.direction != direction && htlc.add.id == htlcId) match { - case Some(htlc) if direction == OUT => spec.copy(toRemoteMsat = spec.toRemoteMsat + htlc.add.amountMsat, htlcs = spec.htlcs - htlc) - case Some(htlc) if direction == IN => spec.copy(toLocalMsat = spec.toLocalMsat + htlc.add.amountMsat, htlcs = spec.htlcs - htlc) + case Some(htlc) if direction == OUT => spec.copy(toRemote = spec.toRemote + htlc.add.amountMsat, htlcs = spec.htlcs - htlc) + case Some(htlc) if direction == IN => spec.copy(toLocal = spec.toLocal + htlc.add.amountMsat, htlcs = spec.htlcs - htlc) case None => throw new RuntimeException(s"cannot find htlc id=${htlcId}") } } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala b/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala index 0febeaab9e..9ea53c8a1b 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala @@ -21,7 +21,8 @@ import java.nio.ByteOrder import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey, ripemd160} import fr.acinq.bitcoin.Script._ import fr.acinq.bitcoin.SigVersion._ -import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Crypto, LexicographicalOrdering, MilliSatoshi, OutPoint, Protocol, SIGHASH_ALL, Satoshi, Script, ScriptElt, ScriptFlags, ScriptWitness, Transaction, TxIn, TxOut, millisatoshi2satoshi} +import fr.acinq.bitcoin._ +import fr.acinq.eclair._ import fr.acinq.eclair.transactions.Scripts._ import fr.acinq.eclair.wire.UpdateAddHtlc import scodec.bits.ByteVector @@ -121,7 +122,7 @@ object Transactions { val htlcTimeoutFee = weight2fee(spec.feeratePerKw, htlcTimeoutWeight) spec.htlcs .filter(_.direction == OUT) - .filter(htlc => MilliSatoshi(htlc.add.amountMsat) >= (dustLimit + htlcTimeoutFee)) + .filter(htlc => htlc.add.amountMsat >= (dustLimit + htlcTimeoutFee).toMilliSatoshi) .toSeq } @@ -129,7 +130,7 @@ object Transactions { val htlcSuccessFee = weight2fee(spec.feeratePerKw, htlcSuccessWeight) spec.htlcs .filter(_.direction == IN) - .filter(htlc => MilliSatoshi(htlc.add.amountMsat) >= (dustLimit + htlcSuccessFee)) + .filter(htlc => htlc.add.amountMsat >= (dustLimit + htlcSuccessFee).toMilliSatoshi) .toSeq } @@ -190,18 +191,18 @@ object Transactions { val commitFee = commitTxFee(localDustLimit, spec) val (toLocalAmount: Satoshi, toRemoteAmount: Satoshi) = if (localIsFunder) { - (millisatoshi2satoshi(MilliSatoshi(spec.toLocalMsat)) - commitFee, millisatoshi2satoshi(MilliSatoshi(spec.toRemoteMsat))) + (spec.toLocal.truncateToSatoshi - commitFee, spec.toRemote.truncateToSatoshi) } else { - (millisatoshi2satoshi(MilliSatoshi(spec.toLocalMsat)), millisatoshi2satoshi(MilliSatoshi(spec.toRemoteMsat)) - commitFee) + (spec.toLocal.truncateToSatoshi, spec.toRemote.truncateToSatoshi - commitFee) } // NB: we don't care if values are < 0, they will be trimmed if they are < dust limit anyway val toLocalDelayedOutput_opt = if (toLocalAmount >= localDustLimit) Some(TxOut(toLocalAmount, pay2wsh(toLocalDelayed(localRevocationPubkey, toLocalDelay, localDelayedPaymentPubkey)))) else None val toRemoteOutput_opt = if (toRemoteAmount >= localDustLimit) Some(TxOut(toRemoteAmount, pay2wpkh(remotePaymentPubkey))) else None val htlcOfferedOutputs = trimOfferedHtlcs(localDustLimit, spec) - .map(htlc => TxOut(MilliSatoshi(htlc.add.amountMsat), pay2wsh(htlcOffered(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.add.paymentHash.bytes))))) + .map(htlc => TxOut(htlc.add.amountMsat.truncateToSatoshi, pay2wsh(htlcOffered(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.add.paymentHash.bytes))))) val htlcReceivedOutputs = trimReceivedHtlcs(localDustLimit, spec) - .map(htlc => TxOut(MilliSatoshi(htlc.add.amountMsat), pay2wsh(htlcReceived(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.add.paymentHash.bytes), htlc.add.cltvExpiry)))) + .map(htlc => TxOut(htlc.add.amountMsat.truncateToSatoshi, pay2wsh(htlcReceived(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.add.paymentHash.bytes), htlc.add.cltvExpiry)))) val txnumber = obscuredCommitTxNumber(commitTxNumber, localIsFunder, localPaymentBasePoint, remotePaymentBasePoint) val (sequence, locktime) = encodeTxNumber(txnumber) @@ -218,8 +219,8 @@ object Transactions { val fee = weight2fee(feeratePerKw, htlcTimeoutWeight) val redeemScript = htlcOffered(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.paymentHash.bytes)) val pubkeyScript = write(pay2wsh(redeemScript)) - val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(Satoshi(htlc.amountMsat / 1000))) - val amount = MilliSatoshi(htlc.amountMsat) - fee + val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(htlc.amountMsat.truncateToSatoshi)) + val amount = htlc.amountMsat.truncateToSatoshi - fee if (amount < localDustLimit) { throw AmountBelowDustLimit } @@ -235,8 +236,8 @@ object Transactions { val fee = weight2fee(feeratePerKw, htlcSuccessWeight) val redeemScript = htlcReceived(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.paymentHash.bytes), htlc.cltvExpiry) val pubkeyScript = write(pay2wsh(redeemScript)) - val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(Satoshi(htlc.amountMsat / 1000))) - val amount = MilliSatoshi(htlc.amountMsat) - fee + val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(htlc.amountMsat.truncateToSatoshi)) + val amount = htlc.amountMsat.truncateToSatoshi - fee if (amount < localDustLimit) { throw AmountBelowDustLimit } @@ -266,7 +267,7 @@ object Transactions { def makeClaimHtlcSuccessTx(commitTx: Transaction, outputsAlreadyUsed: Set[Int], localDustLimit: Satoshi, localHtlcPubkey: PublicKey, remoteHtlcPubkey: PublicKey, remoteRevocationPubkey: PublicKey, localFinalScriptPubKey: ByteVector, htlc: UpdateAddHtlc, feeratePerKw: Long): ClaimHtlcSuccessTx = { val redeemScript = htlcOffered(remoteHtlcPubkey, localHtlcPubkey, remoteRevocationPubkey, ripemd160(htlc.paymentHash.bytes)) val pubkeyScript = write(pay2wsh(redeemScript)) - val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(Satoshi(htlc.amountMsat / 1000))) + val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(htlc.amountMsat.truncateToSatoshi)) val input = InputInfo(OutPoint(commitTx, outputIndex), commitTx.txOut(outputIndex), write(redeemScript)) val tx = Transaction( @@ -289,7 +290,7 @@ object Transactions { def makeClaimHtlcTimeoutTx(commitTx: Transaction, outputsAlreadyUsed: Set[Int], localDustLimit: Satoshi, localHtlcPubkey: PublicKey, remoteHtlcPubkey: PublicKey, remoteRevocationPubkey: PublicKey, localFinalScriptPubKey: ByteVector, htlc: UpdateAddHtlc, feeratePerKw: Long): ClaimHtlcTimeoutTx = { val redeemScript = htlcReceived(remoteHtlcPubkey, localHtlcPubkey, remoteRevocationPubkey, ripemd160(htlc.paymentHash.bytes), htlc.cltvExpiry) val pubkeyScript = write(pay2wsh(redeemScript)) - val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(Satoshi(htlc.amountMsat / 1000))) + val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(htlc.amountMsat.truncateToSatoshi)) val input = InputInfo(OutPoint(commitTx, outputIndex), commitTx.txOut(outputIndex), write(redeemScript)) // unsigned tx @@ -447,9 +448,9 @@ object Transactions { require(spec.htlcs.isEmpty, "there shouldn't be any pending htlcs") val (toLocalAmount: Satoshi, toRemoteAmount: Satoshi) = if (localIsFunder) { - (millisatoshi2satoshi(MilliSatoshi(spec.toLocalMsat)) - closingFee, millisatoshi2satoshi(MilliSatoshi(spec.toRemoteMsat))) + (spec.toLocal.truncateToSatoshi - closingFee, spec.toRemote.truncateToSatoshi) } else { - (millisatoshi2satoshi(MilliSatoshi(spec.toLocalMsat)), millisatoshi2satoshi(MilliSatoshi(spec.toRemoteMsat)) - closingFee) + (spec.toLocal.truncateToSatoshi, spec.toRemote.truncateToSatoshi - closingFee) } // NB: we don't care if values are < 0, they will be trimmed if they are < dust limit anyway val toLocalOutput_opt = if (toLocalAmount >= dustLimit) Some(TxOut(toLocalAmount, localScriptPubKey)) else None diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/ChannelCodecs.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/ChannelCodecs.scala index 97937c05de..df95cd7972 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/ChannelCodecs.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/ChannelCodecs.scala @@ -61,10 +61,10 @@ object ChannelCodecs extends Logging { val localParamsCodec: Codec[LocalParams] = ( ("nodeId" | publicKey) :: ("channelPath" | keyPathCodec) :: - ("dustLimitSatoshis" | uint64overflow) :: + ("dustLimit" | satoshi) :: ("maxHtlcValueInFlightMsat" | uint64) :: - ("channelReserveSatoshis" | uint64overflow) :: - ("htlcMinimumMsat" | uint64overflow) :: + ("channelReserve" | satoshi) :: + ("htlcMinimum" | millisatoshi) :: ("toSelfDelay" | uint16) :: ("maxAcceptedHtlcs" | uint16) :: ("isFunder" | bool) :: @@ -74,10 +74,10 @@ object ChannelCodecs extends Logging { val remoteParamsCodec: Codec[RemoteParams] = ( ("nodeId" | publicKey) :: - ("dustLimitSatoshis" | uint64overflow) :: + ("dustLimit" | satoshi) :: ("maxHtlcValueInFlightMsat" | uint64) :: - ("channelReserveSatoshis" | uint64overflow) :: - ("htlcMinimumMsat" | uint64overflow) :: + ("channelReserve" | satoshi) :: + ("htlcMinimum" | millisatoshi) :: ("toSelfDelay" | uint16) :: ("maxAcceptedHtlcs" | uint16) :: ("fundingPubKey" | publicKey) :: @@ -105,8 +105,8 @@ object ChannelCodecs extends Logging { val commitmentSpecCodec: Codec[CommitmentSpec] = ( ("htlcs" | setCodec(htlcCodec)) :: ("feeratePerKw" | uint32) :: - ("toLocalMsat" | uint64overflow) :: - ("toRemoteMsat" | uint64overflow)).as[CommitmentSpec] + ("toLocal" | millisatoshi) :: + ("toRemote" | millisatoshi)).as[CommitmentSpec] val outPointCodec: Codec[OutPoint] = variableSizeBytes(uint16, bytes.xmap(d => OutPoint.read(d.toArray), d => OutPoint.write(d))) @@ -186,8 +186,8 @@ object ChannelCodecs extends Logging { val relayedCodec: Codec[Relayed] = ( ("originChannelId" | bytes32) :: ("originHtlcId" | int64) :: - ("amountMsatIn" | uint64overflow) :: - ("amountMsatOut" | uint64overflow)).as[Relayed] + ("amountIn" | millisatoshi) :: + ("amountOut" | millisatoshi)).as[Relayed] // this is for backward compatibility to handle legacy payments that didn't have identifiers val UNKNOWN_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000") diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/CommonCodecs.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/CommonCodecs.scala index e56a3645fd..fc749c8fe8 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/CommonCodecs.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/CommonCodecs.scala @@ -19,9 +19,9 @@ package fr.acinq.eclair.wire import java.net.{Inet4Address, Inet6Address, InetAddress} import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin.{ByteVector32, ByteVector64} import fr.acinq.eclair.crypto.Mac32 -import fr.acinq.eclair.{ShortChannelId, UInt64} +import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Satoshi} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, UInt64} import org.apache.commons.codec.binary.Base32 import scodec.bits.{BitVector, ByteVector} import scodec.codecs._ @@ -52,9 +52,11 @@ object CommonCodecs { // this codec can be safely used for values < 2^63 and will fail otherwise // (for something smarter see https://github.com/yzernik/bitcoin-scodec/blob/master/src/main/scala/io/github/yzernik/bitcoinscodec/structures/UInt64.scala) val uint64overflow: Codec[Long] = int64.narrow(l => if (l >= 0) Attempt.Successful(l) else Attempt.failure(Err(s"overflow for value $l")), l => l) - val uint64: Codec[UInt64] = bytes(8).xmap(b => UInt64(b), a => a.toByteVector.padLeft(8)) + val satoshi: Codec[Satoshi] = uint64overflow.xmapc(l => Satoshi(l))(_.toLong) + val millisatoshi: Codec[MilliSatoshi] = uint64overflow.xmapc(l => MilliSatoshi(l))(_.amount) + /** * We impose a minimal encoding on some values (such as varint and truncated int) to ensure that signed hashes can be * re-computed correctly. diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/FailureMessage.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/FailureMessage.scala index a7cee716dd..db816407be 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/FailureMessage.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/FailureMessage.scala @@ -17,8 +17,9 @@ package fr.acinq.eclair.wire import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.eclair.MilliSatoshi import fr.acinq.eclair.crypto.Mac32 -import fr.acinq.eclair.wire.CommonCodecs.{sha256, uint64overflow} +import fr.acinq.eclair.wire.CommonCodecs.{sha256, millisatoshi} import fr.acinq.eclair.wire.LightningMessageCodecs.{channelUpdateCodec, lightningMessageCodec} import scodec.codecs._ import scodec.{Attempt, Codec} @@ -47,16 +48,16 @@ case class TemporaryChannelFailure(update: ChannelUpdate) extends Update { def m case object PermanentChannelFailure extends Perm { def message = "channel is permanently unavailable" } case object RequiredChannelFeatureMissing extends Perm { def message = "channel requires features not present in the onion" } case object UnknownNextPeer extends Perm { def message = "processing node does not know the next peer in the route" } -case class AmountBelowMinimum(amountMsat: Long, update: ChannelUpdate) extends Update { def message = s"payment amount was below the minimum required by the channel" } -case class FeeInsufficient(amountMsat: Long, update: ChannelUpdate) extends Update { def message = s"payment fee was below the minimum required by the channel" } +case class AmountBelowMinimum(amount: MilliSatoshi, update: ChannelUpdate) extends Update { def message = s"payment amount was below the minimum required by the channel" } +case class FeeInsufficient(amount: MilliSatoshi, update: ChannelUpdate) extends Update { def message = s"payment fee was below the minimum required by the channel" } case class ChannelDisabled(messageFlags: Byte, channelFlags: Byte, update: ChannelUpdate) extends Update { def message = "channel is currently disabled" } case class IncorrectCltvExpiry(expiry: Long, update: ChannelUpdate) extends Update { def message = "payment expiry doesn't match the value in the onion" } -case class IncorrectOrUnknownPaymentDetails(amountMsat: Long) extends Perm { def message = "incorrect payment amount or unknown payment hash" } +case class IncorrectOrUnknownPaymentDetails(amount: MilliSatoshi) extends Perm { def message = "incorrect payment amount or unknown payment hash" } case object IncorrectPaymentAmount extends Perm { def message = "payment amount is incorrect" } case class ExpiryTooSoon(update: ChannelUpdate) extends Update { def message = "payment expiry is too close to the current block height for safe handling by the relaying node" } case object FinalExpiryTooSoon extends FailureMessage { def message = "payment expiry is too close to the current block height for safe handling by the final node" } case class FinalIncorrectCltvExpiry(expiry: Long) extends FailureMessage { def message = "payment expiry doesn't match the value in the onion" } -case class FinalIncorrectHtlcAmount(amountMsat: Long) extends FailureMessage { def message = "payment amount is incorrect in the final htlc" } +case class FinalIncorrectHtlcAmount(amount: MilliSatoshi) extends FailureMessage { def message = "payment amount is incorrect in the final htlc" } case object ExpiryTooFar extends FailureMessage { def message = "payment expiry is too far in the future" } // @formatter:on @@ -85,16 +86,16 @@ object FailureMessageCodecs { .typecase(PERM | 8, provide(PermanentChannelFailure)) .typecase(PERM | 9, provide(RequiredChannelFeatureMissing)) .typecase(PERM | 10, provide(UnknownNextPeer)) - .typecase(UPDATE | 11, (("amountMsat" | uint64overflow) :: ("channelUpdate" | channelUpdateWithLengthCodec)).as[AmountBelowMinimum]) - .typecase(UPDATE | 12, (("amountMsat" | uint64overflow) :: ("channelUpdate" | channelUpdateWithLengthCodec)).as[FeeInsufficient]) + .typecase(UPDATE | 11, (("amountMsat" | millisatoshi) :: ("channelUpdate" | channelUpdateWithLengthCodec)).as[AmountBelowMinimum]) + .typecase(UPDATE | 12, (("amountMsat" | millisatoshi) :: ("channelUpdate" | channelUpdateWithLengthCodec)).as[FeeInsufficient]) .typecase(UPDATE | 13, (("expiry" | uint32) :: ("channelUpdate" | channelUpdateWithLengthCodec)).as[IncorrectCltvExpiry]) .typecase(UPDATE | 14, ("channelUpdate" | channelUpdateWithLengthCodec).as[ExpiryTooSoon]) .typecase(UPDATE | 20, (("messageFlags" | byte) :: ("channelFlags" | byte) :: ("channelUpdate" | channelUpdateWithLengthCodec)).as[ChannelDisabled]) - .typecase(PERM | 15, ("amountMsat" | withDefaultValue(optional(bitsRemaining, uint64overflow), 0L)).as[IncorrectOrUnknownPaymentDetails]) + .typecase(PERM | 15, ("amountMsat" | withDefaultValue(optional(bitsRemaining, millisatoshi), MilliSatoshi(0))).as[IncorrectOrUnknownPaymentDetails]) .typecase(PERM | 16, provide(IncorrectPaymentAmount)) .typecase(17, provide(FinalExpiryTooSoon)) .typecase(18, ("expiry" | uint32).as[FinalIncorrectCltvExpiry]) - .typecase(19, ("amountMsat" | uint64overflow).as[FinalIncorrectHtlcAmount]) + .typecase(19, ("amountMsat" | millisatoshi).as[FinalIncorrectHtlcAmount]) .typecase(21, provide(ExpiryTooFar)) /** diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageCodecs.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageCodecs.scala index b7e3891c43..055d9ffa16 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageCodecs.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageCodecs.scala @@ -16,9 +16,11 @@ package fr.acinq.eclair.wire -import fr.acinq.eclair.wire +import fr.acinq.eclair.crypto.Sphinx +import fr.acinq.eclair.{MilliSatoshi, wire} import fr.acinq.eclair.wire.CommonCodecs._ import scodec.Codec +import scodec.bits.ByteVector import scodec.codecs._ /** @@ -51,12 +53,12 @@ object LightningMessageCodecs { val openChannelCodec: Codec[OpenChannel] = ( ("chainHash" | bytes32) :: ("temporaryChannelId" | bytes32) :: - ("fundingSatoshis" | uint64overflow) :: - ("pushMsat" | uint64overflow) :: - ("dustLimitSatoshis" | uint64overflow) :: + ("fundingSatoshis" | satoshi) :: + ("pushMsat" | millisatoshi) :: + ("dustLimitSatoshis" | satoshi) :: ("maxHtlcValueInFlightMsat" | uint64) :: - ("channelReserveSatoshis" | uint64overflow) :: - ("htlcMinimumMsat" | uint64overflow) :: + ("channelReserveSatoshis" | satoshi) :: + ("htlcMinimumMsat" | millisatoshi) :: ("feeratePerKw" | uint32) :: ("toSelfDelay" | uint16) :: ("maxAcceptedHtlcs" | uint16) :: @@ -70,10 +72,10 @@ object LightningMessageCodecs { val acceptChannelCodec: Codec[AcceptChannel] = ( ("temporaryChannelId" | bytes32) :: - ("dustLimitSatoshis" | uint64overflow) :: + ("dustLimitSatoshis" | satoshi) :: ("maxHtlcValueInFlightMsat" | uint64) :: - ("channelReserveSatoshis" | uint64overflow) :: - ("htlcMinimumMsat" | uint64overflow) :: + ("channelReserveSatoshis" | satoshi) :: + ("htlcMinimumMsat" | millisatoshi) :: ("minimumDepth" | uint32) :: ("toSelfDelay" | uint16) :: ("maxAcceptedHtlcs" | uint16) :: @@ -104,13 +106,13 @@ object LightningMessageCodecs { val closingSignedCodec: Codec[ClosingSigned] = ( ("channelId" | bytes32) :: - ("feeSatoshis" | uint64overflow) :: + ("feeSatoshis" | satoshi) :: ("signature" | bytes64)).as[ClosingSigned] val updateAddHtlcCodec: Codec[UpdateAddHtlc] = ( ("channelId" | bytes32) :: ("id" | uint64overflow) :: - ("amountMsat" | uint64overflow) :: + ("amountMsat" | millisatoshi) :: ("paymentHash" | bytes32) :: ("expiry" | uint32) :: ("onionRoutingPacket" | OnionCodecs.paymentOnionPacketCodec)).as[UpdateAddHtlc] @@ -189,10 +191,10 @@ object LightningMessageCodecs { (("messageFlags" | byte) >>:~ { messageFlags => ("channelFlags" | byte) :: ("cltvExpiryDelta" | uint16) :: - ("htlcMinimumMsat" | uint64overflow) :: - ("feeBaseMsat" | uint32) :: + ("htlcMinimumMsat" | millisatoshi) :: + ("feeBaseMsat" | uint32.xmapc(l => MilliSatoshi(l))(_.amount)) :: ("feeProportionalMillionths" | uint32) :: - ("htlcMaximumMsat" | conditional((messageFlags & 1) != 0, uint64overflow)) :: + ("htlcMaximumMsat" | conditional((messageFlags & 1) != 0, millisatoshi)) :: ("unknownFields" | bytes) }) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageTypes.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageTypes.scala index 89be06884a..57d3738918 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageTypes.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageTypes.scala @@ -20,9 +20,9 @@ import java.net.{Inet4Address, Inet6Address, InetAddress, InetSocketAddress} import java.nio.charset.StandardCharsets import com.google.common.base.Charsets +import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Satoshi} import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin.{ByteVector32, ByteVector64} -import fr.acinq.eclair.{ShortChannelId, UInt64} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, UInt64} import scodec.bits.ByteVector import scala.util.Try @@ -68,12 +68,12 @@ case class ChannelReestablish(channelId: ByteVector32, case class OpenChannel(chainHash: ByteVector32, temporaryChannelId: ByteVector32, - fundingSatoshis: Long, - pushMsat: Long, - dustLimitSatoshis: Long, + fundingSatoshis: Satoshi, + pushMsat: MilliSatoshi, + dustLimitSatoshis: Satoshi, maxHtlcValueInFlightMsat: UInt64, - channelReserveSatoshis: Long, - htlcMinimumMsat: Long, + channelReserveSatoshis: Satoshi, + htlcMinimumMsat: MilliSatoshi, feeratePerKw: Long, toSelfDelay: Int, maxAcceptedHtlcs: Int, @@ -86,10 +86,10 @@ case class OpenChannel(chainHash: ByteVector32, channelFlags: Byte) extends ChannelMessage with HasTemporaryChannelId with HasChainHash case class AcceptChannel(temporaryChannelId: ByteVector32, - dustLimitSatoshis: Long, + dustLimitSatoshis: Satoshi, maxHtlcValueInFlightMsat: UInt64, - channelReserveSatoshis: Long, - htlcMinimumMsat: Long, + channelReserveSatoshis: Satoshi, + htlcMinimumMsat: MilliSatoshi, minimumDepth: Long, toSelfDelay: Int, maxAcceptedHtlcs: Int, @@ -115,12 +115,12 @@ case class Shutdown(channelId: ByteVector32, scriptPubKey: ByteVector) extends ChannelMessage with HasChannelId case class ClosingSigned(channelId: ByteVector32, - feeSatoshis: Long, + feeSatoshis: Satoshi, signature: ByteVector64) extends ChannelMessage with HasChannelId case class UpdateAddHtlc(channelId: ByteVector32, id: Long, - amountMsat: Long, + amountMsat: MilliSatoshi, paymentHash: ByteVector32, cltvExpiry: Long, onionRoutingPacket: OnionRoutingPacket) extends HtlcMessage with UpdateMessage with HasChannelId @@ -217,10 +217,10 @@ case class ChannelUpdate(signature: ByteVector64, messageFlags: Byte, channelFlags: Byte, cltvExpiryDelta: Int, - htlcMinimumMsat: Long, - feeBaseMsat: Long, + htlcMinimumMsat: MilliSatoshi, + feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, - htlcMaximumMsat: Option[Long], + htlcMaximumMsat: Option[MilliSatoshi], unknownFields: ByteVector = ByteVector.empty) extends RoutingMessage with HasTimestamp with HasChainHash { require(((messageFlags & 1) != 0) == htlcMaximumMsat.isDefined, "htlcMaximumMsat is not consistent with messageFlags") } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/Onion.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/Onion.scala index 32d942f547..716f11b16e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/Onion.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/Onion.scala @@ -17,7 +17,7 @@ package fr.acinq.eclair.wire import fr.acinq.bitcoin.ByteVector32 -import fr.acinq.eclair.ShortChannelId +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId} import fr.acinq.eclair.crypto.Sphinx import scodec.bits.{BitVector, ByteVector} import scodec.codecs._ @@ -33,7 +33,7 @@ case class OnionRoutingPacket(version: Int, hmac: ByteVector32) case class PerHopPayload(shortChannelId: ShortChannelId, - amtToForward: Long, + amtToForward: MilliSatoshi, outgoingCltvValue: Long) object OnionCodecs { @@ -49,7 +49,7 @@ object OnionCodecs { val perHopPayloadCodec: Codec[PerHopPayload] = ( ("realm" | constant(ByteVector.fromByte(0))) :: ("short_channel_id" | CommonCodecs.shortchannelid) :: - ("amt_to_forward" | CommonCodecs.uint64overflow) :: + ("amt_to_forward" | CommonCodecs.millisatoshi) :: ("outgoing_cltv_value" | uint32) :: ("unused_with_v0_version_on_header" | ignore(8 * 12))).as[PerHopPayload] diff --git a/eclair-core/src/test/resources/api/usablebalances b/eclair-core/src/test/resources/api/usablebalances index edbd4e5a97..c1ef6b4920 100644 --- a/eclair-core/src/test/resources/api/usablebalances +++ b/eclair-core/src/test/resources/api/usablebalances @@ -1 +1 @@ -[{"remoteNodeId":"03af0ed6052cf28d670665549bc86f4b721c9fdb309d40c58f5811f63966e005d0","shortChannelId":"0x0x1","canSendMsat":100000000,"canReceiveMsat":20000000,"isPublic":true},{"remoteNodeId":"03af0ed6052cf28d670665549bc86f4b721c9fdb309d40c58f5811f63966e005d0","shortChannelId":"0x0x2","canSendMsat":400000000,"canReceiveMsat":30000000,"isPublic":false}] \ No newline at end of file +[{"remoteNodeId":"03af0ed6052cf28d670665549bc86f4b721c9fdb309d40c58f5811f63966e005d0","shortChannelId":"0x0x1","canSend":100000000,"canReceive":20000000,"isPublic":true},{"remoteNodeId":"03af0ed6052cf28d670665549bc86f4b721c9fdb309d40c58f5811f63966e005d0","shortChannelId":"0x0x2","canSend":400000000,"canReceive":30000000,"isPublic":false}] \ No newline at end of file diff --git a/eclair-core/src/test/resources/scenarii/01-offer1.script.expected b/eclair-core/src/test/resources/scenarii/01-offer1.script.expected index c3444f106f..5680bafccc 100644 --- a/eclair-core/src/test/resources/scenarii/01-offer1.script.expected +++ b/eclair-core/src/test/resources/scenarii/01-offer1.script.expected @@ -1,30 +1,30 @@ ***A*** LOCAL COMMITS: Commit 1: - Offered htlcs: (0,1000000) + Offered htlcs: (0,MilliSatoshi(1000000)) Received htlcs: - Balance us: 999000000 - Balance them: 1000000000 + Balance us: MilliSatoshi(999000000) + Balance them: MilliSatoshi(1000000000) Fee rate: 10000 REMOTE COMMITS: Commit 1: Offered htlcs: - Received htlcs: (0,1000000) - Balance us: 1000000000 - Balance them: 999000000 + Received htlcs: (0,MilliSatoshi(1000000)) + Balance us: MilliSatoshi(1000000000) + Balance them: MilliSatoshi(999000000) Fee rate: 10000 ***B*** LOCAL COMMITS: Commit 1: Offered htlcs: - Received htlcs: (0,1000000) - Balance us: 1000000000 - Balance them: 999000000 + Received htlcs: (0,MilliSatoshi(1000000)) + Balance us: MilliSatoshi(1000000000) + Balance them: MilliSatoshi(999000000) Fee rate: 10000 REMOTE COMMITS: Commit 1: - Offered htlcs: (0,1000000) + Offered htlcs: (0,MilliSatoshi(1000000)) Received htlcs: - Balance us: 999000000 - Balance them: 1000000000 + Balance us: MilliSatoshi(999000000) + Balance them: MilliSatoshi(1000000000) Fee rate: 10000 diff --git a/eclair-core/src/test/resources/scenarii/02-offer2.script.expected b/eclair-core/src/test/resources/scenarii/02-offer2.script.expected index b12ec47413..2d71ea0dea 100644 --- a/eclair-core/src/test/resources/scenarii/02-offer2.script.expected +++ b/eclair-core/src/test/resources/scenarii/02-offer2.script.expected @@ -1,30 +1,30 @@ ***A*** LOCAL COMMITS: Commit 1: - Offered htlcs: (0,1000000) (1,2000000) + Offered htlcs: (0,MilliSatoshi(1000000)) (1,MilliSatoshi(2000000)) Received htlcs: - Balance us: 997000000 - Balance them: 1000000000 + Balance us: MilliSatoshi(997000000) + Balance them: MilliSatoshi(1000000000) Fee rate: 10000 REMOTE COMMITS: Commit 1: Offered htlcs: - Received htlcs: (0,1000000) (1,2000000) - Balance us: 1000000000 - Balance them: 997000000 + Received htlcs: (0,MilliSatoshi(1000000)) (1,MilliSatoshi(2000000)) + Balance us: MilliSatoshi(1000000000) + Balance them: MilliSatoshi(997000000) Fee rate: 10000 ***B*** LOCAL COMMITS: Commit 1: Offered htlcs: - Received htlcs: (0,1000000) (1,2000000) - Balance us: 1000000000 - Balance them: 997000000 + Received htlcs: (0,MilliSatoshi(1000000)) (1,MilliSatoshi(2000000)) + Balance us: MilliSatoshi(1000000000) + Balance them: MilliSatoshi(997000000) Fee rate: 10000 REMOTE COMMITS: Commit 1: - Offered htlcs: (0,1000000) (1,2000000) + Offered htlcs: (0,MilliSatoshi(1000000)) (1,MilliSatoshi(2000000)) Received htlcs: - Balance us: 997000000 - Balance them: 1000000000 + Balance us: MilliSatoshi(997000000) + Balance them: MilliSatoshi(1000000000) Fee rate: 10000 diff --git a/eclair-core/src/test/resources/scenarii/03-fulfill1.script.expected b/eclair-core/src/test/resources/scenarii/03-fulfill1.script.expected index 1ffcada1d7..3c3fff0fe3 100644 --- a/eclair-core/src/test/resources/scenarii/03-fulfill1.script.expected +++ b/eclair-core/src/test/resources/scenarii/03-fulfill1.script.expected @@ -3,28 +3,28 @@ LOCAL COMMITS: Commit 2: Offered htlcs: Received htlcs: - Balance us: 999000000 - Balance them: 1001000000 + Balance us: MilliSatoshi(999000000) + Balance them: MilliSatoshi(1001000000) Fee rate: 10000 REMOTE COMMITS: Commit 2: Offered htlcs: Received htlcs: - Balance us: 1001000000 - Balance them: 999000000 + Balance us: MilliSatoshi(1001000000) + Balance them: MilliSatoshi(999000000) Fee rate: 10000 ***B*** LOCAL COMMITS: Commit 2: Offered htlcs: Received htlcs: - Balance us: 1001000000 - Balance them: 999000000 + Balance us: MilliSatoshi(1001000000) + Balance them: MilliSatoshi(999000000) Fee rate: 10000 REMOTE COMMITS: Commit 2: Offered htlcs: Received htlcs: - Balance us: 999000000 - Balance them: 1001000000 + Balance us: MilliSatoshi(999000000) + Balance them: MilliSatoshi(1001000000) Fee rate: 10000 diff --git a/eclair-core/src/test/resources/scenarii/04-two-commits-onedir.script.expected b/eclair-core/src/test/resources/scenarii/04-two-commits-onedir.script.expected index f9eacd72d4..965efa4bc8 100644 --- a/eclair-core/src/test/resources/scenarii/04-two-commits-onedir.script.expected +++ b/eclair-core/src/test/resources/scenarii/04-two-commits-onedir.script.expected @@ -3,28 +3,28 @@ LOCAL COMMITS: Commit 1: Offered htlcs: (0,1000000) (1,2000000) Received htlcs: - Balance us: 997000000 - Balance them: 1000000000 + Balance us: MilliSatoshi(997000000) + Balance them: MilliSatoshi(1000000000) Fee rate: 10000 REMOTE COMMITS: Commit 2: Offered htlcs: Received htlcs: (0,1000000) (1,2000000) - Balance us: 1000000000 - Balance them: 997000000 + Balance us: MilliSatoshi(1000000000) + Balance them: MilliSatoshi(997000000) Fee rate: 10000 ***B*** LOCAL COMMITS: Commit 2: Offered htlcs: Received htlcs: (0,1000000) (1,2000000) - Balance us: 1000000000 - Balance them: 997000000 + Balance us: MilliSatoshi(1000000000) + Balance them: MilliSatoshi(997000000) Fee rate: 10000 REMOTE COMMITS: Commit 1: Offered htlcs: (0,1000000) (1,2000000) Received htlcs: - Balance us: 997000000 - Balance them: 1000000000 + Balance us: MilliSatoshi(997000000) + Balance them: MilliSatoshi(1000000000) Fee rate: 10000 diff --git a/eclair-core/src/test/resources/scenarii/10-offers-crossover.script.expected b/eclair-core/src/test/resources/scenarii/10-offers-crossover.script.expected index dfbeddddb2..51469b487b 100644 --- a/eclair-core/src/test/resources/scenarii/10-offers-crossover.script.expected +++ b/eclair-core/src/test/resources/scenarii/10-offers-crossover.script.expected @@ -1,30 +1,30 @@ ***A*** LOCAL COMMITS: Commit 1: - Offered htlcs: (0,1000000) - Received htlcs: (0,2000000) - Balance us: 999000000 - Balance them: 998000000 + Offered htlcs: (0,MilliSatoshi(1000000)) + Received htlcs: (0,MilliSatoshi(2000000)) + Balance us: MilliSatoshi(999000000) + Balance them: MilliSatoshi(998000000) Fee rate: 10000 REMOTE COMMITS: Commit 2: - Offered htlcs: (0,2000000) - Received htlcs: (0,1000000) - Balance us: 998000000 - Balance them: 999000000 + Offered htlcs: (0,MilliSatoshi(2000000)) + Received htlcs: (0,MilliSatoshi(1000000)) + Balance us: MilliSatoshi(998000000) + Balance them: MilliSatoshi(999000000) Fee rate: 10000 ***B*** LOCAL COMMITS: Commit 2: - Offered htlcs: (0,2000000) - Received htlcs: (0,1000000) - Balance us: 998000000 - Balance them: 999000000 + Offered htlcs: (0,MilliSatoshi(2000000)) + Received htlcs: (0,MilliSatoshi(1000000)) + Balance us: MilliSatoshi(998000000) + Balance them: MilliSatoshi(999000000) Fee rate: 10000 REMOTE COMMITS: Commit 1: - Offered htlcs: (0,1000000) - Received htlcs: (0,2000000) - Balance us: 999000000 - Balance them: 998000000 + Offered htlcs: (0,MilliSatoshi(1000000)) + Received htlcs: (0,MilliSatoshi(2000000)) + Balance us: MilliSatoshi(999000000) + Balance them: MilliSatoshi(998000000) Fee rate: 10000 diff --git a/eclair-core/src/test/resources/scenarii/11-commits-crossover.script.expected b/eclair-core/src/test/resources/scenarii/11-commits-crossover.script.expected index bf1061fa9a..2c52101d82 100644 --- a/eclair-core/src/test/resources/scenarii/11-commits-crossover.script.expected +++ b/eclair-core/src/test/resources/scenarii/11-commits-crossover.script.expected @@ -1,30 +1,30 @@ ***A*** LOCAL COMMITS: Commit 2: - Offered htlcs: (0,1000000) - Received htlcs: (0,2000000) - Balance us: 999000000 - Balance them: 998000000 + Offered htlcs: (0,MilliSatoshi(1000000)) + Received htlcs: (0,MilliSatoshi(2000000)) + Balance us: MilliSatoshi(999000000) + Balance them: MilliSatoshi(998000000) Fee rate: 10000 REMOTE COMMITS: Commit 2: - Offered htlcs: (0,2000000) - Received htlcs: (0,1000000) - Balance us: 998000000 - Balance them: 999000000 + Offered htlcs: (0,MilliSatoshi(2000000)) + Received htlcs: (0,MilliSatoshi(1000000)) + Balance us: MilliSatoshi(998000000) + Balance them: MilliSatoshi(999000000) Fee rate: 10000 ***B*** LOCAL COMMITS: Commit 2: - Offered htlcs: (0,2000000) - Received htlcs: (0,1000000) - Balance us: 998000000 - Balance them: 999000000 + Offered htlcs: (0,MilliSatoshi(2000000)) + Received htlcs: (0,MilliSatoshi(1000000)) + Balance us: MilliSatoshi(998000000) + Balance them: MilliSatoshi(999000000) Fee rate: 10000 REMOTE COMMITS: Commit 2: - Offered htlcs: (0,1000000) - Received htlcs: (0,2000000) - Balance us: 999000000 - Balance them: 998000000 + Offered htlcs: (0,MilliSatoshi(1000000)) + Received htlcs: (0,MilliSatoshi(2000000)) + Balance us: MilliSatoshi(999000000) + Balance them: MilliSatoshi(998000000) Fee rate: 10000 diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/CoinUtilsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/CoinUtilsSpec.scala index ea294912a5..8e051c2c93 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/CoinUtilsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/CoinUtilsSpec.scala @@ -16,7 +16,7 @@ package fr.acinq.eclair -import fr.acinq.bitcoin.{Btc, MilliBtc, MilliSatoshi, Satoshi} +import fr.acinq.bitcoin.{Btc, MilliBtc, Satoshi} import org.scalatest.FunSuite diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala index 2ab1b0b329..728815f5a3 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala @@ -16,18 +16,14 @@ package fr.acinq.eclair -import java.io.File - import akka.actor.ActorSystem import akka.testkit.{TestKit, TestProbe} -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi} -import fr.acinq.bitcoin.{ByteVector32, Crypto} +import fr.acinq.bitcoin.{ByteVector32, Crypto, Satoshi} import akka.util.Timeout import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.eclair.blockchain.TestWallet import fr.acinq.eclair.io.Peer.OpenChannel import fr.acinq.eclair.payment.PaymentLifecycle.{ReceivePayment, SendPayment, SendPaymentToRoute} -import org.scalatest.{Outcome, fixture} import fr.acinq.eclair.payment.PaymentLifecycle.SendPayment import fr.acinq.eclair.payment.PaymentRequest.ExtraHop import org.scalatest.{Matchers, Outcome, fixture} @@ -37,10 +33,8 @@ import fr.acinq.eclair.channel.{CMD_FORCECLOSE, Register} import fr.acinq.eclair.payment.LocalPaymentHandler import fr.acinq.eclair.channel._ import fr.acinq.eclair.db._ -import fr.acinq.eclair.payment.PaymentRequest import fr.acinq.eclair.router.RouteCalculationSpec.makeUpdate import org.mockito.scalatest.IdiomaticMockito - import scala.concurrent.Await import scala.util.{Failure, Success} import scala.concurrent.duration._ @@ -84,12 +78,12 @@ class EclairImplSpec extends TestKit(ActorSystem("mySystem")) with fixture.FunSu val nodeId = PublicKey(hex"030bb6a5e0c6b203c7e2180fb78c7ba4bdce46126761d8201b91ddac089cdecc87") // standard conversion - eclair.open(nodeId, fundingSatoshis = 10000000L, pushMsat_opt = None, fundingFeerateSatByte_opt = Some(5), flags_opt = None, openTimeout_opt = None) + eclair.open(nodeId, fundingAmount = Satoshi(10000000L), pushAmount_opt = None, fundingFeerateSatByte_opt = Some(5), flags_opt = None, openTimeout_opt = None) val open = switchboard.expectMsgType[OpenChannel] assert(open.fundingTxFeeratePerKw_opt == Some(1250)) // check that minimum fee rate of 253 sat/bw is used - eclair.open(nodeId, fundingSatoshis = 10000000L, pushMsat_opt = None, fundingFeerateSatByte_opt = Some(1), flags_opt = None, openTimeout_opt = None) + eclair.open(nodeId, fundingAmount = Satoshi(10000000L), pushAmount_opt = None, fundingFeerateSatByte_opt = Some(1), flags_opt = None, openTimeout_opt = None) val open1 = switchboard.expectMsgType[OpenChannel] assert(open1.fundingTxFeeratePerKw_opt == Some(MinimumFeeratePerKw)) } @@ -100,37 +94,37 @@ class EclairImplSpec extends TestKit(ActorSystem("mySystem")) with fixture.FunSu val eclair = new EclairImpl(kit) val nodeId = PublicKey(hex"030bb6a5e0c6b203c7e2180fb78c7ba4bdce46126761d8201b91ddac089cdecc87") - eclair.send(recipientNodeId = nodeId, amountMsat = 123, paymentHash = ByteVector32.Zeroes, assistedRoutes = Seq.empty, minFinalCltvExpiry_opt = None) + eclair.send(recipientNodeId = nodeId, amount = MilliSatoshi(123), paymentHash = ByteVector32.Zeroes, assistedRoutes = Seq.empty, minFinalCltvExpiry_opt = None) val send = paymentInitiator.expectMsgType[SendPayment] assert(send.targetNodeId == nodeId) - assert(send.amountMsat == 123) + assert(send.amount == MilliSatoshi(123)) assert(send.paymentHash == ByteVector32.Zeroes) assert(send.assistedRoutes == Seq.empty) // with assisted routes val hints = Seq(Seq(ExtraHop(Bob.nodeParams.nodeId, ShortChannelId("569178x2331x1"), feeBaseMsat = 10, feeProportionalMillionths = 1, cltvExpiryDelta = 12))) - eclair.send(recipientNodeId = nodeId, amountMsat = 123, paymentHash = ByteVector32.Zeroes, assistedRoutes = hints, minFinalCltvExpiry_opt = None) + eclair.send(recipientNodeId = nodeId, amount = MilliSatoshi(123), paymentHash = ByteVector32.Zeroes, assistedRoutes = hints, minFinalCltvExpiry_opt = None) val send1 = paymentInitiator.expectMsgType[SendPayment] assert(send1.targetNodeId == nodeId) - assert(send1.amountMsat == 123) + assert(send1.amount == MilliSatoshi(123)) assert(send1.paymentHash == ByteVector32.Zeroes) assert(send1.assistedRoutes == hints) // with finalCltvExpiry - eclair.send(recipientNodeId = nodeId, amountMsat = 123, paymentHash = ByteVector32.Zeroes, assistedRoutes = Seq.empty, minFinalCltvExpiry_opt = Some(96)) + eclair.send(recipientNodeId = nodeId, amount = MilliSatoshi(123), paymentHash = ByteVector32.Zeroes, assistedRoutes = Seq.empty, minFinalCltvExpiry_opt = Some(96)) val send2 = paymentInitiator.expectMsgType[SendPayment] assert(send2.targetNodeId == nodeId) - assert(send2.amountMsat == 123) + assert(send2.amount == MilliSatoshi(123)) assert(send2.paymentHash == ByteVector32.Zeroes) assert(send2.finalCltvExpiry == 96) // with custom route fees parameters - eclair.send(recipientNodeId = nodeId, amountMsat = 123, paymentHash = ByteVector32.Zeroes, assistedRoutes = Seq.empty, minFinalCltvExpiry_opt = None, feeThresholdSat_opt = Some(123), maxFeePct_opt = Some(4.20)) + eclair.send(recipientNodeId = nodeId, amount = MilliSatoshi(123), paymentHash = ByteVector32.Zeroes, assistedRoutes = Seq.empty, minFinalCltvExpiry_opt = None, feeThreshold_opt = Some(Satoshi(123)), maxFeePct_opt = Some(4.20)) val send3 = paymentInitiator.expectMsgType[SendPayment] assert(send3.targetNodeId == nodeId) - assert(send3.amountMsat == 123) + assert(send3.amount == MilliSatoshi(123)) assert(send3.paymentHash == ByteVector32.Zeroes) - assert(send3.routeParams.get.maxFeeBaseMsat == 123 * 1000) // conversion sat -> msat + assert(send3.routeParams.get.maxFeeBase == Satoshi(123).toMilliSatoshi) // conversion sat -> msat assert(send3.routeParams.get.maxFeePct == 4.20) } @@ -140,11 +134,11 @@ class EclairImplSpec extends TestKit(ActorSystem("mySystem")) with fixture.FunSu val (a, b, c, d, e) = (randomKey.publicKey, randomKey.publicKey, randomKey.publicKey, randomKey.publicKey, randomKey.publicKey) val updates = List( - makeUpdate(1L, a, b, feeBaseMsat = 0, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 13), - makeUpdate(4L, a, e, feeBaseMsat = 0, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 12), - makeUpdate(2L, b, c, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 500), - makeUpdate(3L, c, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 500), - makeUpdate(7L, e, c, feeBaseMsat = 2, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 12) + makeUpdate(1L, a, b, feeBase = MilliSatoshi(0), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 13), + makeUpdate(4L, a, e, feeBase = MilliSatoshi(0), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 12), + makeUpdate(2L, b, c, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 500), + makeUpdate(3L, c, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 500), + makeUpdate(7L, e, c, feeBase = MilliSatoshi(2), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 12) ).toMap val eclair = new EclairImpl(kit) @@ -188,15 +182,15 @@ class EclairImplSpec extends TestKit(ActorSystem("mySystem")) with fixture.FunSu val fallBackAddressRaw = "muhtvdmsnbQEPFuEmxcChX58fGvXaaUoVt" val eclair = new EclairImpl(kit) - eclair.receive("some desc", Some(123L), Some(456), Some(fallBackAddressRaw), None) + eclair.receive("some desc", Some(MilliSatoshi(123L)), Some(456), Some(fallBackAddressRaw), None) val receive = paymentHandler.expectMsgType[ReceivePayment] - assert(receive.amountMsat_opt == Some(MilliSatoshi(123L))) + assert(receive.amount_opt == Some(MilliSatoshi(123L))) assert(receive.expirySeconds_opt == Some(456)) assert(receive.fallbackAddress == Some(fallBackAddressRaw)) // try with wrong address format - assertThrows[IllegalArgumentException](eclair.receive("some desc", Some(123L), Some(456), Some("wassa wassa"), None)) + assertThrows[IllegalArgumentException](eclair.receive("some desc", Some(MilliSatoshi(123L)), Some(456), Some("wassa wassa"), None)) } test("passing a payment_preimage to /createinvoice should result in an invoice with payment_hash=H(payment_preimage)") { fixture => @@ -206,7 +200,7 @@ class EclairImplSpec extends TestKit(ActorSystem("mySystem")) with fixture.FunSu val eclair = new EclairImpl(kitWithPaymentHandler) val paymentPreimage = randomBytes32 - val fResp = eclair.receive(description = "some desc", amountMsat_opt = None, expire_opt = None, fallbackAddress_opt = None, paymentPreimage_opt = Some(paymentPreimage)) + val fResp = eclair.receive(description = "some desc", amount_opt = None, expire_opt = None, fallbackAddress_opt = None, paymentPreimage_opt = Some(paymentPreimage)) awaitCond({ fResp.value match { case Some(Success(pr)) => pr.paymentHash == Crypto.sha256(paymentPreimage) @@ -252,12 +246,12 @@ class EclairImplSpec extends TestKit(ActorSystem("mySystem")) with fixture.FunSu val route = Seq(PublicKey(hex"030bb6a5e0c6b203c7e2180fb78c7ba4bdce46126761d8201b91ddac089cdecc87")) val eclair = new EclairImpl(kit) - eclair.sendToRoute(route, 1234, ByteVector32.One, 123) + eclair.sendToRoute(route, MilliSatoshi(1234), ByteVector32.One, 123) val send = paymentInitiator.expectMsgType[SendPaymentToRoute] assert(send.hops == route) - assert(send.amountMsat == 1234) + assert(send.amount == MilliSatoshi(1234)) assert(send.finalCltvExpiry == 123) assert(send.paymentHash == ByteVector32.One) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala b/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala index 3f5e0b3744..9c340e5a8e 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala @@ -19,7 +19,7 @@ package fr.acinq.eclair import java.sql.{Connection, DriverManager} import fr.acinq.bitcoin.Crypto.PrivateKey -import fr.acinq.bitcoin.{Block, ByteVector32, Script} +import fr.acinq.bitcoin.{Block, ByteVector32, Satoshi, Script} import fr.acinq.eclair.NodeParams.BITCOIND import fr.acinq.eclair.blockchain.fee.{FeeEstimator, FeeTargets, FeeratesPerKw, OnChainFeeConf} import fr.acinq.eclair.crypto.LocalKeyManager @@ -36,8 +36,8 @@ import scala.concurrent.duration._ */ object TestConstants { - val fundingSatoshis = 1000000L - val pushMsat = 200000000L + val fundingSatoshis = Satoshi(1000000L) + val pushMsat = MilliSatoshi(200000000L) val feeratePerKw = 10000L val emptyOnionPacket = wire.OnionRoutingPacket(0, ByteVector.fill(33)(0), ByteVector.fill(1300)(0), ByteVector32.Zeroes) @@ -70,7 +70,7 @@ object TestConstants { globalFeatures = ByteVector.empty, localFeatures = ByteVector(0), overrideFeatures = Map.empty, - dustLimitSatoshis = 1100, + dustLimit = Satoshi(1100), onChainFeeConf = OnChainFeeConf( feeTargets = FeeTargets(6, 2, 2, 6), feeEstimator = new TestFeeEstimator, @@ -81,11 +81,11 @@ object TestConstants { maxAcceptedHtlcs = 100, expiryDeltaBlocks = 144, fulfillSafetyBeforeTimeoutBlocks = 6, - htlcMinimumMsat = 0, + htlcMinimum = MilliSatoshi(0), minDepthBlocks = 3, toRemoteDelayBlocks = 144, maxToLocalDelayBlocks = 1000, - feeBaseMsat = 546000, + feeBase = MilliSatoshi(546000), feeProportionalMillionth = 10, reserveToFundingRatio = 0.01, // note: not used (overridden below) maxReserveToFundingRatio = 0.05, @@ -101,12 +101,12 @@ object TestConstants { channelFlags = 1, watcherType = BITCOIND, paymentRequestExpiry = 1 hour, - minFundingSatoshis = 1000L, + minFundingSatoshis = Satoshi(1000L), routerConf = RouterConf( randomizeRouteSelection = false, channelExcludeDuration = 60 seconds, routerBroadcastInterval = 5 seconds, - searchMaxFeeBaseSat = 21, + searchMaxFeeBase = Satoshi(21), searchMaxFeePct = 0.03, searchMaxCltv = 2016, searchMaxRouteLength = 20, @@ -124,7 +124,7 @@ object TestConstants { defaultFinalScriptPubKey = Script.write(Script.pay2wpkh(PrivateKey(randomBytes32).publicKey)), isFunder = true, fundingSatoshis).copy( - channelReserveSatoshis = 10000 // Bob will need to keep that much satoshis as direct payment + channelReserve = Satoshi(10000) // Bob will need to keep that much satoshis as direct payment ) } @@ -140,7 +140,7 @@ object TestConstants { globalFeatures = ByteVector.empty, localFeatures = ByteVector.empty, // no announcement overrideFeatures = Map.empty, - dustLimitSatoshis = 1000, + dustLimit = Satoshi(1000), onChainFeeConf = OnChainFeeConf( feeTargets = FeeTargets(6, 2, 2, 6), feeEstimator = new TestFeeEstimator, @@ -151,11 +151,11 @@ object TestConstants { maxAcceptedHtlcs = 30, expiryDeltaBlocks = 144, fulfillSafetyBeforeTimeoutBlocks = 6, - htlcMinimumMsat = 1000, + htlcMinimum = MilliSatoshi(1000), minDepthBlocks = 3, toRemoteDelayBlocks = 144, maxToLocalDelayBlocks = 1000, - feeBaseMsat = 546000, + feeBase = MilliSatoshi(546000), feeProportionalMillionth = 10, reserveToFundingRatio = 0.01, // note: not used (overridden below) maxReserveToFundingRatio = 0.05, @@ -171,12 +171,12 @@ object TestConstants { channelFlags = 1, watcherType = BITCOIND, paymentRequestExpiry = 1 hour, - minFundingSatoshis = 1000L, + minFundingSatoshis = Satoshi(1000L), routerConf = RouterConf( randomizeRouteSelection = false, channelExcludeDuration = 60 seconds, routerBroadcastInterval = 5 seconds, - searchMaxFeeBaseSat = 21, + searchMaxFeeBase = Satoshi(21), searchMaxFeePct = 0.03, searchMaxCltv = 2016, searchMaxRouteLength = 20, @@ -194,7 +194,7 @@ object TestConstants { defaultFinalScriptPubKey = Script.write(Script.pay2wpkh(PrivateKey(randomBytes32).publicKey)), isFunder = false, fundingSatoshis).copy( - channelReserveSatoshis = 20000 // Alice will need to keep that much satoshis as direct payment + channelReserve = Satoshi(20000) // Alice will need to keep that much satoshis as direct payment ) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala index 4c3f5d9e9d..87fbffeefe 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala @@ -27,7 +27,7 @@ import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest, WSProbe import akka.stream.ActorMaterializer import akka.util.Timeout import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi} +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.eclair.TestConstants._ import fr.acinq.eclair._ import fr.acinq.eclair.io.NodeURI @@ -39,6 +39,7 @@ import org.json4s.jackson.Serialization import org.mockito.scalatest.IdiomaticMockito import org.scalatest.{FunSuite, Matchers} import scodec.bits._ + import scala.concurrent.Future import scala.concurrent.duration._ import scala.io.Source @@ -147,8 +148,8 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock val eclair = mock[Eclair] val mockService = new MockService(eclair) eclair.usableBalances()(any[Timeout]) returns Future.successful(List( - UsableBalances(canSendMsat = 100000000, canReceiveMsat = 20000000, shortChannelId = ShortChannelId(1), remoteNodeId = TestConstants.Alice.keyManager.nodeKey.publicKey, isPublic = true), - UsableBalances(canSendMsat = 400000000, canReceiveMsat = 30000000, shortChannelId = ShortChannelId(2), remoteNodeId = TestConstants.Alice.keyManager.nodeKey.publicKey, isPublic = false) + UsableBalances(canSend = MilliSatoshi(100000000), canReceive = MilliSatoshi(20000000), shortChannelId = ShortChannelId(1), remoteNodeId = TestConstants.Alice.keyManager.nodeKey.publicKey, isPublic = true), + UsableBalances(canSend = MilliSatoshi(400000000), canReceive = MilliSatoshi(30000000), shortChannelId = ShortChannelId(2), remoteNodeId = TestConstants.Alice.keyManager.nodeKey.publicKey, isPublic = false) )) Post("/usablebalances") ~> @@ -269,7 +270,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock check { assert(handled) assert(status == OK) - eclair.send(any, 1258000, any, any, any, any, any, any)(any[Timeout]).wasCalled(once) + eclair.send(any, MilliSatoshi(1258000), any, any, any, any, any, any)(any[Timeout]).wasCalled(once) } @@ -279,7 +280,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock check { assert(handled) assert(status == OK) - eclair.send(any, 123, any, any, any, any, Some(112233), Some(2.34))(any[Timeout]).wasCalled(once) + eclair.send(any, MilliSatoshi(123), any, any, any, any, Some(Satoshi(112233)), Some(2.34))(any[Timeout]).wasCalled(once) } } @@ -311,7 +312,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock val jsonNodes = serialization.write(expectedRoute) val eclair = mock[Eclair] - eclair.sendToRoute(any[List[PublicKey]], anyLong, any[ByteVector32], anyLong)(any[Timeout]) returns Future.successful(paymentUUID) + eclair.sendToRoute(any[List[PublicKey]], any[MilliSatoshi], any[ByteVector32], anyLong)(any[Timeout]) returns Future.successful(paymentUUID) val mockService = new MockService(eclair) Post("/sendtoroute", FormData("route" -> jsonNodes, "amountMsat" -> "1234", "paymentHash" -> ByteVector32.Zeroes.toHex, "finalCltvExpiry" -> "190").toEntity) ~> @@ -322,7 +323,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock assert(handled) assert(status == OK) assert(entityAs[String] == "\""+rawUUID+"\"") - eclair.sendToRoute(expectedRoute, 1234, ByteVector32.Zeroes, 190)(any[Timeout]).wasCalled(once) + eclair.sendToRoute(expectedRoute, MilliSatoshi(1234), ByteVector32.Zeroes, 190)(any[Timeout]).wasCalled(once) } // this test uses CSV encoded route @@ -334,7 +335,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock assert(handled) assert(status == OK) assert(entityAs[String] == "\""+rawUUID+"\"") - eclair.sendToRoute(expectedRoute, 1234, ByteVector32.One, 190)(any[Timeout]).wasCalled(once) + eclair.sendToRoute(expectedRoute, MilliSatoshi(1234), ByteVector32.One, 190)(any[Timeout]).wasCalled(once) } } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/api/JsonSerializersSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/api/JsonSerializersSpec.scala index 4313c853e6..38a6b6d7cc 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/api/JsonSerializersSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/api/JsonSerializersSpec.scala @@ -19,7 +19,7 @@ package fr.acinq.eclair.api import java.net.InetAddress import java.util.UUID -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi, OutPoint, Transaction} +import fr.acinq.bitcoin.{ByteVector32, OutPoint, Transaction} import fr.acinq.eclair._ import fr.acinq.eclair.payment.{PaymentRequest, PaymentSettlingOnChain} import fr.acinq.eclair.api.JsonSupport.CustomTypeHints diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreWalletSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreWalletSpec.scala index 3af10b6426..50fb3666ef 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreWalletSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreWalletSpec.scala @@ -257,7 +257,7 @@ class BitcoinCoreWalletSpec extends TestKit(ActorSystem("test")) with BitcoindSe val tx1 = Transaction.read(signedTx1) // let's then generate another tx that double spends the first one val inputs = tx1.txIn.map(txIn => Map("txid" -> txIn.outPoint.txid.toString, "vout" -> txIn.outPoint.index)).toArray - bitcoinClient.invoke("createrawtransaction", inputs, Map(address -> tx1.txOut.map(_.amount.toLong).sum * 1.0 / 1e8)).pipeTo(sender.ref) + bitcoinClient.invoke("createrawtransaction", inputs, Map(address -> tx1.txOut.map(_.amount).sum.toLong * 1.0 / 1e8)).pipeTo(sender.ref) val JString(unsignedtx2) = sender.expectMsgType[JValue] bitcoinClient.invoke("signrawtransactionwithwallet", unsignedtx2).pipeTo(sender.ref) val JString(signedTx2) = sender.expectMsgType[JValue] \ "hex" diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/FuzzySpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/FuzzySpec.scala index d44d98dc94..188ceada39 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/FuzzySpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/FuzzySpec.scala @@ -22,7 +22,7 @@ import java.util.concurrent.CountDownLatch import akka.actor.{Actor, ActorLogging, ActorRef, Props, Status} import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi} +import fr.acinq.bitcoin.{ByteVector32} import fr.acinq.eclair.TestConstants.{Alice, Bob} import fr.acinq.eclair._ import fr.acinq.eclair.blockchain._ @@ -93,7 +93,7 @@ class FuzzySpec extends TestkitBaseClass with StateTestsHelperMethods with Loggi def buildCmdAdd(paymentHash: ByteVector32, dest: PublicKey) = { // allow overpaying (no more than 2 times the required amount) - val amount = requiredAmount + Random.nextInt(requiredAmount) + val amount = MilliSatoshi(requiredAmount + Random.nextInt(requiredAmount)) val expiry = Globals.blockCount.get().toInt + Channel.MIN_CLTV_EXPIRY + 1 PaymentLifecycle.buildCommand(UUID.randomUUID(), amount, expiry, paymentHash, Hop(null, dest, null) :: Nil)._1 } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/ThroughputSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/ThroughputSpec.scala index 9ff1329b8e..08aa1f88d6 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/ThroughputSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/ThroughputSpec.scala @@ -51,7 +51,7 @@ class ThroughputSpec extends FunSuite { case ('add, tgt: ActorRef) => val r = randomBytes32 val h = Crypto.sha256(r) - tgt ! CMD_ADD_HTLC(1, h, 1, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + tgt ! CMD_ADD_HTLC(MilliSatoshi(1), h, 1, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) context.become(run(h2r + (h -> r))) case ('sig, tgt: ActorRef) => tgt ! CMD_SIGN diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/StateTestsHelperMethods.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/StateTestsHelperMethods.scala index ac21eeba31..e614c054d7 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/StateTestsHelperMethods.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/StateTestsHelperMethods.scala @@ -20,6 +20,7 @@ import java.util.UUID import akka.testkit.{TestFSMRef, TestKitBase, TestProbe} import fr.acinq.bitcoin.{ByteVector32, Crypto} +import fr.acinq.eclair import fr.acinq.eclair.TestConstants.{Alice, Bob, TestFeeEstimator} import fr.acinq.eclair.blockchain._ import fr.acinq.eclair.blockchain.fee.FeeTargets @@ -29,6 +30,7 @@ import fr.acinq.eclair.payment.PaymentLifecycle import fr.acinq.eclair.router.Hop import fr.acinq.eclair.wire._ import fr.acinq.eclair.{Globals, NodeParams, TestConstants, randomBytes32} +import fr.acinq.eclair._ /** * Created by PM on 23/08/2016. @@ -66,7 +68,7 @@ trait StateTestsHelperMethods extends TestKitBase { tags: Set[String] = Set.empty): Unit = { import setup._ val channelFlags = if (tags.contains("channels_public")) ChannelFlags.AnnounceChannel else ChannelFlags.Empty - val pushMsat = if (tags.contains("no_push_msat")) 0 else TestConstants.pushMsat + val pushMsat = if (tags.contains("no_push_msat")) MilliSatoshi(0) else TestConstants.pushMsat val (aliceParams, bobParams) = (Alice.channelParams, Bob.channelParams) val aliceInit = Init(aliceParams.globalFeatures, aliceParams.localFeatures) val bobInit = Init(bobParams.globalFeatures, bobParams.localFeatures) @@ -98,19 +100,19 @@ trait StateTestsHelperMethods extends TestKitBase { bob2blockchain.expectMsgType[WatchConfirmed] // deeply buried awaitCond(alice.stateName == NORMAL) awaitCond(bob.stateName == NORMAL) - assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.availableBalanceForSendMsat == math.max(pushMsat - TestConstants.Alice.channelParams.channelReserveSatoshis * 1000, 0)) + assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.availableBalanceForSend == maxOf(pushMsat - TestConstants.Alice.channelParams.channelReserve.toMilliSatoshi, MilliSatoshi(0))) // x2 because alice and bob share the same relayer channelUpdateListener.expectMsgType[LocalChannelUpdate] channelUpdateListener.expectMsgType[LocalChannelUpdate] } - def addHtlc(amountMsat: Int, s: TestFSMRef[State, Data, Channel], r: TestFSMRef[State, Data, Channel], s2r: TestProbe, r2s: TestProbe): (ByteVector32, UpdateAddHtlc) = { + def addHtlc(amount: MilliSatoshi, s: TestFSMRef[State, Data, Channel], r: TestFSMRef[State, Data, Channel], s2r: TestProbe, r2s: TestProbe): (ByteVector32, UpdateAddHtlc) = { val R: ByteVector32 = randomBytes32 val H: ByteVector32 = Crypto.sha256(R) val sender = TestProbe() val receiverPubkey = r.underlyingActor.nodeParams.nodeId val expiry = 400144 - val cmd = PaymentLifecycle.buildCommand(UUID.randomUUID, amountMsat, expiry, H, Hop(null, receiverPubkey, null) :: Nil)._1.copy(commit = false) + val cmd = PaymentLifecycle.buildCommand(UUID.randomUUID, amount, expiry, H, Hop(null, receiverPubkey, null) :: Nil)._1.copy(commit = false) sender.send(s, cmd) sender.expectMsg("ok") val htlc = s2r.expectMsgType[UpdateAddHtlc] diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForAcceptChannelStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForAcceptChannelStateSpec.scala index b71fd9de45..980384bc58 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForAcceptChannelStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForAcceptChannelStateSpec.scala @@ -83,7 +83,7 @@ class WaitForAcceptChannelStateSpec extends TestkitBaseClass with StateTestsHelp import f._ val accept = bob2alice.expectMsgType[AcceptChannel] // we don't want their dust limit to be below 546 - val lowDustLimitSatoshis = 545 + val lowDustLimitSatoshis = Satoshi(545) alice ! accept.copy(dustLimitSatoshis = lowDustLimitSatoshis) val error = alice2bob.expectMsgType[Error] assert(error === Error(accept.temporaryChannelId, DustLimitTooSmall(accept.temporaryChannelId, lowDustLimitSatoshis, Channel.MIN_DUSTLIMIT).getMessage)) @@ -104,7 +104,7 @@ class WaitForAcceptChannelStateSpec extends TestkitBaseClass with StateTestsHelp import f._ val accept = bob2alice.expectMsgType[AcceptChannel] // 30% is huge, recommended ratio is 1% - val reserveTooHigh = (0.3 * TestConstants.fundingSatoshis).toLong + val reserveTooHigh = Satoshi((0.3 * TestConstants.fundingSatoshis.toLong).toLong) alice ! accept.copy(channelReserveSatoshis = reserveTooHigh) val error = alice2bob.expectMsgType[Error] assert(error === Error(accept.temporaryChannelId, ChannelReserveTooHigh(accept.temporaryChannelId, reserveTooHigh, 0.3, 0.05).getMessage)) @@ -114,7 +114,7 @@ class WaitForAcceptChannelStateSpec extends TestkitBaseClass with StateTestsHelp test("recv AcceptChannel (reserve below dust limit)") { f => import f._ val accept = bob2alice.expectMsgType[AcceptChannel] - val reserveTooSmall = accept.dustLimitSatoshis - 1 + val reserveTooSmall = accept.dustLimitSatoshis - Satoshi(1) alice ! accept.copy(channelReserveSatoshis = reserveTooSmall) val error = alice2bob.expectMsgType[Error] assert(error === Error(accept.temporaryChannelId, DustLimitTooLarge(accept.temporaryChannelId, accept.dustLimitSatoshis, reserveTooSmall).getMessage)) @@ -125,7 +125,7 @@ class WaitForAcceptChannelStateSpec extends TestkitBaseClass with StateTestsHelp import f._ val accept = bob2alice.expectMsgType[AcceptChannel] val open = alice.stateData.asInstanceOf[DATA_WAIT_FOR_ACCEPT_CHANNEL].lastSent - val reserveTooSmall = open.dustLimitSatoshis - 1 + val reserveTooSmall = open.dustLimitSatoshis - Satoshi(1) alice ! accept.copy(channelReserveSatoshis = reserveTooSmall) val error = alice2bob.expectMsgType[Error] assert(error === Error(accept.temporaryChannelId, ChannelReserveBelowOurDustLimit(accept.temporaryChannelId, reserveTooSmall, open.dustLimitSatoshis).getMessage)) @@ -136,7 +136,7 @@ class WaitForAcceptChannelStateSpec extends TestkitBaseClass with StateTestsHelp import f._ val accept = bob2alice.expectMsgType[AcceptChannel] val open = alice.stateData.asInstanceOf[DATA_WAIT_FOR_ACCEPT_CHANNEL].lastSent - val dustTooBig = open.channelReserveSatoshis + 1 + val dustTooBig = open.channelReserveSatoshis + Satoshi(1) alice ! accept.copy(dustLimitSatoshis = dustTooBig) val error = alice2bob.expectMsgType[Error] assert(error === Error(accept.temporaryChannelId, DustLimitAboveOurChannelReserve(accept.temporaryChannelId, dustTooBig, open.channelReserveSatoshis).getMessage)) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForOpenChannelStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForOpenChannelStateSpec.scala index 6b74dfd654..549ee6f692 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForOpenChannelStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForOpenChannelStateSpec.scala @@ -17,12 +17,12 @@ package fr.acinq.eclair.channel.states.a import akka.testkit.{TestFSMRef, TestProbe} -import fr.acinq.bitcoin.{Block, ByteVector32} +import fr.acinq.bitcoin.{Block, ByteVector32, Satoshi} import fr.acinq.eclair.TestConstants.{Alice, Bob} import fr.acinq.eclair.channel._ import fr.acinq.eclair.channel.states.StateTestsHelperMethods import fr.acinq.eclair.wire.{Error, Init, OpenChannel} -import fr.acinq.eclair.{TestConstants, TestkitBaseClass} +import fr.acinq.eclair.{MilliSatoshi, TestConstants, TestkitBaseClass, ToMilliSatoshiConversion} import org.scalatest.Outcome import scala.concurrent.duration._ @@ -69,20 +69,20 @@ class WaitForOpenChannelStateSpec extends TestkitBaseClass with StateTestsHelper test("recv OpenChannel (funding too low)") { f => import f._ val open = alice2bob.expectMsgType[OpenChannel] - val lowFundingMsat = 100 - bob ! open.copy(fundingSatoshis = lowFundingMsat) + val lowFunding = Satoshi(100) + bob ! open.copy(fundingSatoshis = lowFunding) val error = bob2alice.expectMsgType[Error] - assert(error === Error(open.temporaryChannelId, InvalidFundingAmount(open.temporaryChannelId, lowFundingMsat, Bob.nodeParams.minFundingSatoshis, Channel.MAX_FUNDING_SATOSHIS).getMessage)) + assert(error === Error(open.temporaryChannelId, InvalidFundingAmount(open.temporaryChannelId, lowFunding, Bob.nodeParams.minFundingSatoshis, Channel.MAX_FUNDING).getMessage)) awaitCond(bob.stateName == CLOSED) } test("recv OpenChannel (funding too high)") { f => import f._ val open = alice2bob.expectMsgType[OpenChannel] - val highFundingMsat = 100000000 + val highFundingMsat = Satoshi(100000000) bob ! open.copy(fundingSatoshis = highFundingMsat) val error = bob2alice.expectMsgType[Error] - assert(error === Error(open.temporaryChannelId, InvalidFundingAmount(open.temporaryChannelId, highFundingMsat, Bob.nodeParams.minFundingSatoshis, Channel.MAX_FUNDING_SATOSHIS).getMessage)) + assert(error.toAscii === Error(open.temporaryChannelId, InvalidFundingAmount(open.temporaryChannelId, highFundingMsat, Bob.nodeParams.minFundingSatoshis, Channel.MAX_FUNDING).getMessage).toAscii) awaitCond(bob.stateName == CLOSED) } @@ -99,10 +99,10 @@ class WaitForOpenChannelStateSpec extends TestkitBaseClass with StateTestsHelper test("recv OpenChannel (invalid push_msat)") { f => import f._ val open = alice2bob.expectMsgType[OpenChannel] - val invalidPushMsat = 100000000000L + val invalidPushMsat = MilliSatoshi(100000000000L) bob ! open.copy(pushMsat = invalidPushMsat) val error = bob2alice.expectMsgType[Error] - assert(error === Error(open.temporaryChannelId, InvalidPushAmount(open.temporaryChannelId, invalidPushMsat, 1000 * open.fundingSatoshis).getMessage)) + assert(error === Error(open.temporaryChannelId, InvalidPushAmount(open.temporaryChannelId, invalidPushMsat, open.fundingSatoshis.toMilliSatoshi).getMessage)) awaitCond(bob.stateName == CLOSED) } @@ -120,7 +120,7 @@ class WaitForOpenChannelStateSpec extends TestkitBaseClass with StateTestsHelper import f._ val open = alice2bob.expectMsgType[OpenChannel] // 30% is huge, recommended ratio is 1% - val reserveTooHigh = (0.3 * TestConstants.fundingSatoshis).toLong + val reserveTooHigh = Satoshi((0.3 * TestConstants.fundingSatoshis.toLong).toLong) bob ! open.copy(channelReserveSatoshis = reserveTooHigh) val error = bob2alice.expectMsgType[Error] assert(error === Error(open.temporaryChannelId, ChannelReserveTooHigh(open.temporaryChannelId, reserveTooHigh, 0.3, 0.05).getMessage)) @@ -155,7 +155,7 @@ class WaitForOpenChannelStateSpec extends TestkitBaseClass with StateTestsHelper test("recv OpenChannel (reserve below dust)") { f => import f._ val open = alice2bob.expectMsgType[OpenChannel] - val reserveTooSmall = open.dustLimitSatoshis - 1 + val reserveTooSmall = open.dustLimitSatoshis - Satoshi(1) bob ! open.copy(channelReserveSatoshis = reserveTooSmall) val error = bob2alice.expectMsgType[Error] // we check that the error uses the temporary channel id @@ -166,12 +166,12 @@ class WaitForOpenChannelStateSpec extends TestkitBaseClass with StateTestsHelper test("recv OpenChannel (toLocal + toRemote below reserve)") { f => import f._ val open = alice2bob.expectMsgType[OpenChannel] - val fundingSatoshis = open.channelReserveSatoshis + 499 + val fundingSatoshis = open.channelReserveSatoshis.toLong + 499 val pushMsat = 500 * 1000 - bob ! open.copy(fundingSatoshis = fundingSatoshis, pushMsat = pushMsat) + bob ! open.copy(fundingSatoshis = Satoshi(fundingSatoshis), pushMsat = MilliSatoshi(pushMsat)) val error = bob2alice.expectMsgType[Error] // we check that the error uses the temporary channel id - assert(error === Error(open.temporaryChannelId, ChannelReserveNotMet(open.temporaryChannelId, 500 * 1000, (open.channelReserveSatoshis - 1) * 1000, open.channelReserveSatoshis).getMessage)) + assert(error === Error(open.temporaryChannelId, ChannelReserveNotMet(open.temporaryChannelId, Satoshi(500).toMilliSatoshi, (open.channelReserveSatoshis - Satoshi(1)).toMilliSatoshi, open.channelReserveSatoshis).getMessage)) awaitCond(bob.stateName == CLOSED) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForFundingCreatedStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForFundingCreatedStateSpec.scala index 3bc38c87cf..9a88921c3d 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForFundingCreatedStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForFundingCreatedStateSpec.scala @@ -17,14 +17,14 @@ package fr.acinq.eclair.channel.states.b import akka.testkit.{TestFSMRef, TestProbe} -import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.eclair.TestConstants.{Alice, Bob} import fr.acinq.eclair.blockchain._ import fr.acinq.eclair.channel._ import fr.acinq.eclair.channel.states.StateTestsHelperMethods import fr.acinq.eclair.transactions.Transactions import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{TestConstants, TestkitBaseClass} +import fr.acinq.eclair.{MilliSatoshi, TestConstants, TestkitBaseClass} import org.scalatest.{Outcome, Tag} import scala.concurrent.duration._ @@ -41,7 +41,7 @@ class WaitForFundingCreatedStateSpec extends TestkitBaseClass with StateTestsHel val setup = init() import setup._ val (fundingSatoshis, pushMsat) = if (test.tags.contains("funder_below_reserve")) { - (1000100L, 1000000000L) // toRemote = 100 satoshis + (Satoshi(1000100L), MilliSatoshi(1000000000L)) // toRemote = 100 satoshis } else { (TestConstants.fundingSatoshis, TestConstants.pushMsat) } @@ -71,13 +71,13 @@ class WaitForFundingCreatedStateSpec extends TestkitBaseClass with StateTestsHel test("recv FundingCreated (funder can't pay fees)", Tag("funder_below_reserve")) { f => import f._ - val fees = Transactions.commitWeight * TestConstants.feeratePerKw / 1000 - val reserve = Bob.channelParams.channelReserveSatoshis - val missing = 100 - fees - reserve + val fees = Satoshi(Transactions.commitWeight * TestConstants.feeratePerKw / 1000) + val reserve = Bob.channelParams.channelReserve + val missing = Satoshi(100) - fees - reserve val fundingCreated = alice2bob.expectMsgType[FundingCreated] alice2bob.forward(bob) val error = bob2alice.expectMsgType[Error] - assert(error === Error(fundingCreated.temporaryChannelId, s"can't pay the fee: missingSatoshis=${-1 * missing} reserveSatoshis=$reserve feesSatoshis=$fees")) + assert(error === Error(fundingCreated.temporaryChannelId, s"can't pay the fee: missing=${-missing} reserve=$reserve fees=$fees")) awaitCond(bob.stateName == CLOSED) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala index e327d9cad6..2300d25e05 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala @@ -23,6 +23,7 @@ import akka.actor.Status.Failure import akka.testkit.TestProbe import fr.acinq.bitcoin.Crypto.PrivateKey import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Crypto, Satoshi, ScriptFlags, Transaction} +import fr.acinq.eclair._ import fr.acinq.eclair.TestConstants.{Alice, Bob, TestFeeEstimator} import fr.acinq.eclair.UInt64.Conversions._ import fr.acinq.eclair.blockchain._ @@ -69,7 +70,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, h, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(50000000), h, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) sender.expectMsg("ok") val htlc = alice2bob.expectMsgType[UpdateAddHtlc] @@ -87,7 +88,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, h, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(50000000), h, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") val htlc = alice2bob.expectMsgType[UpdateAddHtlc] assert(htlc.id == i && htlc.paymentHash == h) @@ -99,8 +100,8 @@ 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, cltvExpiry = 400144, paymentHash = h, onionRoutingPacket = TestConstants.emptyOnionPacket) - val cmd = CMD_ADD_HTLC(originHtlc.amountMsat - 10000, h, originHtlc.cltvExpiry - 7, TestConstants.emptyOnionPacket, upstream = Right(originHtlc)) + val originHtlc = UpdateAddHtlc(channelId = randomBytes32, id = 5656, amountMsat = MilliSatoshi(50000000), cltvExpiry = 400144, paymentHash = h, onionRoutingPacket = TestConstants.emptyOnionPacket) + val cmd = CMD_ADD_HTLC(originHtlc.amountMsat - MilliSatoshi(10000), h, originHtlc.cltvExpiry - 7, TestConstants.emptyOnionPacket, upstream = Right(originHtlc)) sender.send(alice, cmd) sender.expectMsg("ok") val htlc = alice2bob.expectMsgType[UpdateAddHtlc] @@ -119,7 +120,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] val currentBlockCount = Globals.blockCount.get val expiryTooSmall = currentBlockCount + 3 - val add = CMD_ADD_HTLC(500000000, randomBytes32, expiryTooSmall, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(500000000), randomBytes32, expiryTooSmall, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = ExpiryTooSmall(channelId(alice), currentBlockCount + Channel.MIN_CLTV_EXPIRY, expiryTooSmall, currentBlockCount) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) @@ -132,7 +133,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] val currentBlockCount = Globals.blockCount.get val expiryTooBig = currentBlockCount + Channel.MAX_CLTV_EXPIRY + 1 - val add = CMD_ADD_HTLC(500000000, randomBytes32, expiryTooBig, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(500000000), randomBytes32, expiryTooBig, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = ExpiryTooBig(channelId(alice), maximum = currentBlockCount + Channel.MAX_CLTV_EXPIRY, actual = expiryTooBig, blockCount = currentBlockCount) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) @@ -143,9 +144,9 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - val add = CMD_ADD_HTLC(50, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(50), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) - val error = HtlcValueTooSmall(channelId(alice), 1000, 50) + val error = HtlcValueTooSmall(channelId(alice), MilliSatoshi(1000), MilliSatoshi(50)) 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) } @@ -154,9 +155,9 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - val add = CMD_ADD_HTLC(Int.MaxValue, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(Int.MaxValue), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) - val error = InsufficientFunds(channelId(alice), amountMsat = Int.MaxValue, missingSatoshis = 1376443, reserveSatoshis = 20000, feesSatoshis = 8960) + val error = InsufficientFunds(channelId(alice), amount = MilliSatoshi(Int.MaxValue), missing = Satoshi(1376443), reserve = Satoshi(20000), fees = Satoshi(8960)) 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) } @@ -165,18 +166,18 @@ 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, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(500000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] - sender.send(alice, CMD_ADD_HTLC(200000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(200000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] - sender.send(alice, CMD_ADD_HTLC(67600000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(67600000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] - val add = CMD_ADD_HTLC(1000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(1000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) - val error = InsufficientFunds(channelId(alice), amountMsat = 1000000, missingSatoshis = 1000, reserveSatoshis = 20000, feesSatoshis = 12400) + val error = InsufficientFunds(channelId(alice), amount = MilliSatoshi(1000000), missing = Satoshi(1000), reserve = Satoshi(20000), fees = Satoshi(12400)) 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) } @@ -185,15 +186,15 @@ 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, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(300000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] - sender.send(alice, CMD_ADD_HTLC(300000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(300000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] - val add = CMD_ADD_HTLC(500000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(500000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) - val error = InsufficientFunds(channelId(alice), amountMsat = 500000000, missingSatoshis = 332400, reserveSatoshis = 20000, feesSatoshis = 12400) + val error = InsufficientFunds(channelId(alice), amount = MilliSatoshi(500000000), missing = Satoshi(332400), reserve = Satoshi(20000), fees = Satoshi(12400)) 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) } @@ -202,7 +203,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, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(151000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(bob, add) val error = HtlcValueTooHighInFlight(channelId(bob), maximum = 150000000, actual = 151000000) sender.expectMsg(Failure(AddHtlcFailed(channelId(bob), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) @@ -215,11 +216,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, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(10000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] } - val add = CMD_ADD_HTLC(10000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(10000000), randomBytes32, 400144, 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)))) @@ -230,7 +231,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 * 2 / 3 * 1000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add1 = CMD_ADD_HTLC(TestConstants.fundingSatoshis.toMilliSatoshi * 2 / 3, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add1) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] @@ -238,9 +239,9 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] // this is over channel-capacity - val add2 = CMD_ADD_HTLC(TestConstants.fundingSatoshis * 2 / 3 * 1000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add2 = CMD_ADD_HTLC(TestConstants.fundingSatoshis.toMilliSatoshi * 2 / 3, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add2) - val error = InsufficientFunds(channelId(alice), add2.amountMsat, 564012, 20000, 10680) + val error = InsufficientFunds(channelId(alice), add2.amount, Satoshi(564013), Satoshi(20000), Satoshi(10680)) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add2.paymentHash, error, Local(add2.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add2)))) alice2bob.expectNoMsg(200 millis) } @@ -255,7 +256,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, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(500000000), randomBytes32, 400144, 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)))) @@ -267,14 +268,14 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] // let's make alice send an htlc - val add1 = CMD_ADD_HTLC(500000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add1 = CMD_ADD_HTLC(MilliSatoshi(500000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add1) sender.expectMsg("ok") // at the same time bob initiates a closing sender.send(bob, CMD_CLOSE(None)) sender.expectMsg("ok") // this command will be received by alice right after having received the shutdown - val add2 = CMD_ADD_HTLC(100000000, randomBytes32, 300000, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add2 = CMD_ADD_HTLC(MilliSatoshi(100000000), randomBytes32, 300000, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) // messages cross alice2bob.expectMsgType[UpdateAddHtlc] alice2bob.forward(bob) @@ -288,7 +289,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, randomBytes32, 400144, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(150000), randomBytes32, 400144, 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 @@ -298,7 +299,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, randomBytes32, 400144, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 42, MilliSatoshi(150000), randomBytes32, 400144, TestConstants.emptyOnionPacket) bob ! htlc.copy(id = 0) bob ! htlc.copy(id = 1) bob ! htlc.copy(id = 2) @@ -315,10 +316,10 @@ 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, randomBytes32, cltvExpiry = 400144, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(150), randomBytes32, cltvExpiry = 400144, TestConstants.emptyOnionPacket) alice2bob.forward(bob, htlc) val error = bob2alice.expectMsgType[Error] - assert(new String(error.data.toArray) === HtlcValueTooSmall(channelId(bob), minimum = 1000, actual = 150).getMessage) + assert(new String(error.data.toArray) === HtlcValueTooSmall(channelId(bob), minimum = MilliSatoshi(1000), actual = MilliSatoshi(150)).getMessage) awaitCond(bob.stateName == CLOSING) // channel should be advertised as down assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) @@ -330,10 +331,10 @@ 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, Long.MaxValue, randomBytes32, 400144, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(Long.MaxValue), randomBytes32, 400144, TestConstants.emptyOnionPacket) alice2bob.forward(bob, htlc) val error = bob2alice.expectMsgType[Error] - assert(new String(error.data.toArray) === InsufficientFunds(channelId(bob), amountMsat = Long.MaxValue, missingSatoshis = 9223372036083735L, reserveSatoshis = 20000, feesSatoshis = 8960).getMessage) + assert(new String(error.data.toArray) === InsufficientFunds(channelId(bob), amount = MilliSatoshi(Long.MaxValue), missing = Satoshi(9223372036083735L), reserve = Satoshi(20000), fees = Satoshi(8960)).getMessage) awaitCond(bob.stateName == CLOSING) // channel should be advertised as down assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) @@ -345,12 +346,12 @@ 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, randomBytes32, 400144, TestConstants.emptyOnionPacket)) - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, 200000000, randomBytes32, 400144, TestConstants.emptyOnionPacket)) - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, 167600000, randomBytes32, 400144, TestConstants.emptyOnionPacket)) - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 3, 10000000, randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(400000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliSatoshi(200000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, MilliSatoshi(167600000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 3, MilliSatoshi(10000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) val error = bob2alice.expectMsgType[Error] - assert(new String(error.data.toArray) === InsufficientFunds(channelId(bob), amountMsat = 10000000, missingSatoshis = 11720, reserveSatoshis = 20000, feesSatoshis = 14120).getMessage) + assert(new String(error.data.toArray) === InsufficientFunds(channelId(bob), amount = MilliSatoshi(10000000), missing = Satoshi(11720), reserve = Satoshi(20000), fees = Satoshi(14120)).getMessage) awaitCond(bob.stateName == CLOSING) // channel should be advertised as down assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) @@ -362,11 +363,11 @@ 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, randomBytes32, 400144, TestConstants.emptyOnionPacket)) - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, 300000000, randomBytes32, 400144, TestConstants.emptyOnionPacket)) - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, 500000000, randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(300000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliSatoshi(300000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, MilliSatoshi(500000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) val error = bob2alice.expectMsgType[Error] - assert(new String(error.data.toArray) === InsufficientFunds(channelId(bob), amountMsat = 500000000, missingSatoshis = 332400, reserveSatoshis = 20000, feesSatoshis = 12400).getMessage) + assert(new String(error.data.toArray) === InsufficientFunds(channelId(bob), amount = MilliSatoshi(500000000), missing = Satoshi(332400), reserve = Satoshi(20000), fees = Satoshi(12400)).getMessage) awaitCond(bob.stateName == CLOSING) // channel should be advertised as down assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) @@ -378,7 +379,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, randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(alice, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(151000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) val error = alice2bob.expectMsgType[Error] assert(new String(error.data.toArray) === HtlcValueTooHighInFlight(channelId(alice), maximum = 150000000, actual = 151000000).getMessage) awaitCond(alice.stateName == CLOSING) @@ -394,9 +395,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, randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, i, MilliSatoshi(1000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) } - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 30, 1000000, randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 30, MilliSatoshi(1000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) val error = bob2alice.expectMsgType[Error] assert(new String(error.data.toArray) === TooManyAcceptedHtlcs(channelId(bob), maximum = 30).getMessage) awaitCond(bob.stateName == CLOSING) @@ -410,7 +411,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_SIGN") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") val commitSig = alice2bob.expectMsgType[CommitSig] @@ -421,7 +422,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, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(10000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] @@ -453,34 +454,34 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() // for the test to be really useful we have constraint on parameters - assert(Alice.nodeParams.dustLimitSatoshis > Bob.nodeParams.dustLimitSatoshis) + assert(Alice.nodeParams.dustLimit > Bob.nodeParams.dustLimit) // we're gonna exchange two htlcs in each direction, the goal is to have bob's commitment have 4 htlcs, and alice's // commitment only have 3. We will then check that alice indeed persisted 4 htlcs, and bob only 3. - val aliceMinReceive = Alice.nodeParams.dustLimitSatoshis + weight2fee(TestConstants.feeratePerKw, htlcSuccessWeight).toLong - val aliceMinOffer = Alice.nodeParams.dustLimitSatoshis + weight2fee(TestConstants.feeratePerKw, htlcTimeoutWeight).toLong - val bobMinReceive = Bob.nodeParams.dustLimitSatoshis + weight2fee(TestConstants.feeratePerKw, htlcSuccessWeight).toLong - val bobMinOffer = Bob.nodeParams.dustLimitSatoshis + weight2fee(TestConstants.feeratePerKw, htlcTimeoutWeight).toLong - val a2b_1 = bobMinReceive + 10 // will be in alice and bob tx - val a2b_2 = bobMinReceive + 20 // will be in alice and bob tx - val b2a_1 = aliceMinReceive + 10 // will be in alice and bob tx - val b2a_2 = bobMinOffer + 10 // will be only be in bob tx + val aliceMinReceive = Alice.nodeParams.dustLimit + weight2fee(TestConstants.feeratePerKw, htlcSuccessWeight) + val aliceMinOffer = Alice.nodeParams.dustLimit + weight2fee(TestConstants.feeratePerKw, htlcTimeoutWeight) + val bobMinReceive = Bob.nodeParams.dustLimit + weight2fee(TestConstants.feeratePerKw, htlcSuccessWeight) + val bobMinOffer = Bob.nodeParams.dustLimit + weight2fee(TestConstants.feeratePerKw, htlcTimeoutWeight) + val a2b_1 = bobMinReceive + Satoshi(10) // will be in alice and bob tx + val a2b_2 = bobMinReceive + Satoshi(20) // will be in alice and bob tx + val b2a_1 = aliceMinReceive + Satoshi(10) // will be in alice and bob tx + val b2a_2 = bobMinOffer + Satoshi(10) // will be only be in bob tx assert(a2b_1 > aliceMinOffer && a2b_1 > bobMinReceive) 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 * 1000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(a2b_1.toMilliSatoshi, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] alice2bob.forward(bob) - sender.send(alice, CMD_ADD_HTLC(a2b_2 * 1000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(a2b_2.toMilliSatoshi, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] alice2bob.forward(bob) - sender.send(bob, CMD_ADD_HTLC(b2a_1 * 1000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(bob, CMD_ADD_HTLC(b2a_1.toMilliSatoshi, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") bob2alice.expectMsgType[UpdateAddHtlc] bob2alice.forward(alice) - sender.send(bob, CMD_ADD_HTLC(b2a_2 * 1000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(bob, CMD_ADD_HTLC(b2a_2.toMilliSatoshi, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") bob2alice.expectMsgType[UpdateAddHtlc] bob2alice.forward(alice) @@ -495,16 +496,16 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { assert(alice.underlyingActor.nodeParams.db.channels.listHtlcInfos(alice.stateData.asInstanceOf[DATA_NORMAL].channelId, 2).size == 4) assert(bob.underlyingActor.nodeParams.db.channels.listHtlcInfos(bob.stateData.asInstanceOf[DATA_NORMAL].channelId, 0).size == 0) assert(bob.underlyingActor.nodeParams.db.channels.listHtlcInfos(bob.stateData.asInstanceOf[DATA_NORMAL].channelId, 1).size == 3) - } + } test("recv CMD_SIGN (htlcs with same pubkeyScript but different amounts)") { f => import f._ val sender = TestProbe() - val add = CMD_ADD_HTLC(10000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(10000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) val epsilons = List(3, 1, 5, 7, 6) // unordered on purpose val htlcCount = epsilons.size for (i <- epsilons) { - sender.send(alice, add.copy(amountMsat = add.amountMsat + i * 1000)) + sender.send(alice, add.copy(amount = MilliSatoshi(add.amount.toLong + i * 1000))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] alice2bob.forward(bob) @@ -532,7 +533,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_SIGN (while waiting for RevokeAndAck (no pending changes)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.remoteNextCommitInfo.isRight) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -550,7 +551,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_SIGN (while waiting for RevokeAndAck (with pending changes)") { f => import f._ val sender = TestProbe() - val (r1, htlc1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r1, htlc1) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.remoteNextCommitInfo.isRight) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -560,7 +561,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { assert(waitForRevocation.reSignAsap === false) // actual test starts here - val (r2, htlc2) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r2, htlc2) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectNoMsg(300 millis) assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.remoteNextCommitInfo === Left(waitForRevocation.copy(reSignAsap = true))) @@ -572,7 +573,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // channel starts with all funds on alice's side, so channel will be initially disabled on bob's side assert(Announcements.isEnabled(bob.stateData.asInstanceOf[DATA_NORMAL].channelUpdate.channelFlags) === false) // alice will send enough funds to bob to make it go above reserve - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) sender.send(bob, CMD_FULFILL_HTLC(htlc.id, r)) sender.expectMsg("ok") @@ -596,7 +597,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) val initialState = bob.stateData.asInstanceOf[DATA_NORMAL] sender.send(alice, CMD_SIGN) @@ -612,7 +613,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.htlcs.exists(h => h.add.id == htlc.id && h.direction == IN)) assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.htlcTxsAndSigs.size == 1) - assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.toLocalMsat == initialState.commitments.localCommit.spec.toLocalMsat) + assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.toLocal == initialState.commitments.localCommit.spec.toLocal) assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.remoteChanges.acked.size == 0) assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.remoteChanges.signed.size == 1) } @@ -621,7 +622,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) val initialState = bob.stateData.asInstanceOf[DATA_NORMAL] sender.send(alice, CMD_SIGN) @@ -637,26 +638,26 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.htlcs.exists(h => h.add.id == htlc.id && h.direction == OUT)) assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.htlcTxsAndSigs.size == 1) - assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.toLocalMsat == initialState.commitments.localCommit.spec.toLocalMsat) + assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.toLocal == initialState.commitments.localCommit.spec.toLocal) } test("recv CommitSig (multiple htlcs in both directions)") { f => import f._ val sender = TestProbe() - val (r1, htlc1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) // a->b (regular) + val (r1, htlc1) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) // a->b (regular) - val (r2, htlc2) = addHtlc(8000000, alice, bob, alice2bob, bob2alice) // a->b (regular) + val (r2, htlc2) = addHtlc(MilliSatoshi(8000000), alice, bob, alice2bob, bob2alice) // a->b (regular) - val (r3, htlc3) = addHtlc(300000, bob, alice, bob2alice, alice2bob) // b->a (dust) + val (r3, htlc3) = addHtlc(MilliSatoshi(300000), bob, alice, bob2alice, alice2bob) // b->a (dust) - val (r4, htlc4) = addHtlc(1000000, alice, bob, alice2bob, bob2alice) // a->b (regular) + val (r4, htlc4) = addHtlc(MilliSatoshi(1000000), alice, bob, alice2bob, bob2alice) // a->b (regular) - val (r5, htlc5) = addHtlc(50000000, bob, alice, bob2alice, alice2bob) // b->a (regular) + val (r5, htlc5) = addHtlc(MilliSatoshi(50000000), bob, alice, bob2alice, alice2bob) // b->a (regular) - val (r6, htlc6) = addHtlc(500000, alice, bob, alice2bob, bob2alice) // a->b (dust) + val (r6, htlc6) = addHtlc(MilliSatoshi(500000), alice, bob, alice2bob, bob2alice) // a->b (dust) - val (r7, htlc7) = addHtlc(4000000, bob, alice, bob2alice, alice2bob) // b->a (regular) + val (r7, htlc7) = addHtlc(MilliSatoshi(4000000), bob, alice, bob2alice, alice2bob) // b->a (regular) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -698,12 +699,12 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val r = randomBytes32 val h = Crypto.sha256(r) - sender.send(alice, CMD_ADD_HTLC(50000000, h, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(50000000), h, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") val htlc1 = alice2bob.expectMsgType[UpdateAddHtlc] alice2bob.forward(bob) - sender.send(alice, CMD_ADD_HTLC(50000000, h, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(50000000), h, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") val htlc2 = alice2bob.expectMsgType[UpdateAddHtlc] alice2bob.forward(bob) @@ -714,7 +715,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { crossSign(alice, bob, alice2bob, bob2alice) awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.htlcs.exists(h => h.add.id == htlc1.id && h.direction == IN)) assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.htlcTxsAndSigs.size == 2) - assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.toLocalMsat == initialState.commitments.localCommit.spec.toLocalMsat) + assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.toLocal == initialState.commitments.localCommit.spec.toLocal) assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx.txOut.count(_.amount == Satoshi(50000)) == 2) } @@ -737,7 +738,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CommitSig (invalid signature)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx // actual test begins @@ -754,7 +755,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx sender.send(alice, CMD_SIGN) @@ -775,7 +776,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx sender.send(alice, CMD_SIGN) @@ -796,7 +797,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv RevokeAndAck (one htlc sent)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -814,7 +815,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv RevokeAndAck (one htlc received)") { f => import f._ val sender = TestProbe() - val (_, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (_, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -843,19 +844,19 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv RevokeAndAck (multiple htlcs in both directions)") { f => import f._ val sender = TestProbe() - val (r1, htlc1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) // a->b (regular) + val (r1, htlc1) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) // a->b (regular) - val (r2, htlc2) = addHtlc(8000000, alice, bob, alice2bob, bob2alice) // a->b (regular) + val (r2, htlc2) = addHtlc(MilliSatoshi(8000000), alice, bob, alice2bob, bob2alice) // a->b (regular) - val (r3, htlc3) = addHtlc(300000, bob, alice, bob2alice, alice2bob) // b->a (dust) + val (r3, htlc3) = addHtlc(MilliSatoshi(300000), bob, alice, bob2alice, alice2bob) // b->a (dust) - val (r4, htlc4) = addHtlc(1000000, alice, bob, alice2bob, bob2alice) // a->b (regular) + val (r4, htlc4) = addHtlc(MilliSatoshi(1000000), alice, bob, alice2bob, bob2alice) // a->b (regular) - val (r5, htlc5) = addHtlc(50000000, bob, alice, bob2alice, alice2bob) // b->a (regular) + val (r5, htlc5) = addHtlc(MilliSatoshi(50000000), bob, alice, bob2alice, alice2bob) // b->a (regular) - val (r6, htlc6) = addHtlc(500000, alice, bob, alice2bob, bob2alice) // a->b (dust) + val (r6, htlc6) = addHtlc(MilliSatoshi(500000), alice, bob, alice2bob, bob2alice) // a->b (dust) - val (r7, htlc7) = addHtlc(4000000, bob, alice, bob2alice, alice2bob) // b->a (regular) + val (r7, htlc7) = addHtlc(MilliSatoshi(4000000), bob, alice, bob2alice, alice2bob) // b->a (regular) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -880,13 +881,13 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv RevokeAndAck (with reSignAsap=true)") { f => import f._ val sender = TestProbe() - val (r1, htlc1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r1, htlc1) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.remoteNextCommitInfo.isRight) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] alice2bob.forward(bob) - val (r2, htlc2) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r2, htlc2) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectNoMsg(300 millis) assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.remoteNextCommitInfo.left.toOption.get.reSignAsap === true) @@ -901,7 +902,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val tx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -938,7 +939,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv RevokeAndAck (forward UpdateFailHtlc)") { f => import f._ val sender = TestProbe() - val (_, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (_, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) sender.send(bob, CMD_FAIL_HTLC(htlc.id, Right(PermanentChannelFailure))) sender.expectMsg("ok") @@ -967,7 +968,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv RevokeAndAck (forward UpdateFailMalformedHtlc)") { f => import f._ val sender = TestProbe() - val (_, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (_, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) sender.send(bob, CMD_FAIL_MALFORMED_HTLC(htlc.id, Sphinx.PaymentPacket.hash(htlc.onionRoutingPacket), FailureMessageCodecs.BADONION)) sender.expectMsg("ok") @@ -996,7 +997,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv RevocationTimeout") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -1013,7 +1014,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_FULFILL_HTLC") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // actual test begins @@ -1040,7 +1041,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_FULFILL_HTLC (invalid preimage)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // actual test begins @@ -1063,7 +1064,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateFulfillHtlc") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) sender.send(bob, CMD_FULFILL_HTLC(htlc.id, r)) sender.expectMsg("ok") @@ -1083,7 +1084,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateFulfillHtlc (sender has not signed htlc)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] @@ -1117,7 +1118,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateFulfillHtlc (invalid preimage)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) relayerB.expectMsgType[ForwardAdd] val tx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx @@ -1138,7 +1139,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_FAIL_HTLC") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // actual test begins @@ -1176,7 +1177,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_FAIL_MALFORMED_HTLC") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // actual test begins @@ -1221,7 +1222,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateFailHtlc") { f => import f._ val sender = TestProbe() - val (_, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (_, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) sender.send(bob, CMD_FAIL_HTLC(htlc.id, Right(PermanentChannelFailure))) sender.expectMsg("ok") @@ -1241,7 +1242,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val sender = TestProbe() // Alice sends an HTLC to Bob, which they both sign - val (_, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (_, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // Bob fails the HTLC because he cannot parse it val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] @@ -1268,7 +1269,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateFailMalformedHtlc (invalid failure_code)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // actual test begins @@ -1288,7 +1289,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateFailHtlc (sender has not signed htlc)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] @@ -1396,7 +1397,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { bob.feeEstimator.setFeerate(FeeratesPerKw.single(fee.feeratePerKw)) sender.send(bob, fee) val error = bob2alice.expectMsgType[Error] - assert(new String(error.data.toArray) === CannotAffordFees(channelId(bob), missingSatoshis = 71620000L, reserveSatoshis = 20000L, feesSatoshis = 72400000L).getMessage) + assert(new String(error.data.toArray) === CannotAffordFees(channelId(bob), missing = Satoshi(71620000L), reserve = Satoshi(20000L), fees = Satoshi(72400000L)).getMessage) awaitCond(bob.stateName == CLOSING) // channel should be advertised as down assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) @@ -1446,7 +1447,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_UPDATE_RELAY_FEE ") { f => import f._ val sender = TestProbe() - val newFeeBaseMsat = TestConstants.Alice.nodeParams.feeBaseMsat * 2 + val newFeeBaseMsat = TestConstants.Alice.nodeParams.feeBase * 2 val newFeeProportionalMillionth = TestConstants.Alice.nodeParams.feeProportionalMillionth * 2 sender.send(alice, CMD_UPDATE_RELAY_FEE(newFeeBaseMsat, newFeeProportionalMillionth)) sender.expectMsg("ok") @@ -1471,7 +1472,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_CLOSE (with unacked sent htlcs)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_CLOSE(None)) sender.expectMsg(Failure(CannotCloseWithUnsignedOutgoingHtlcs(channelId(bob)))) } @@ -1486,7 +1487,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_CLOSE (with signed sent htlcs)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_CLOSE(None)) sender.expectMsg("ok") @@ -1511,7 +1512,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_CLOSE (while waiting for a RevokeAndAck)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] @@ -1536,7 +1537,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv Shutdown (with unacked sent htlcs)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(bob, CMD_CLOSE(None)) bob2alice.expectMsgType[Shutdown] // actual test begins @@ -1557,7 +1558,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv Shutdown (with unacked received htlcs)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) // actual test begins sender.send(bob, Shutdown(ByteVector32.Zeroes, TestConstants.Alice.channelParams.defaultFinalScriptPubKey)) bob2alice.expectMsgType[Error] @@ -1581,7 +1582,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv Shutdown (with invalid final script and signed htlcs, in response to a Shutdown)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) sender.send(bob, CMD_CLOSE(None)) bob2alice.expectMsgType[Shutdown] @@ -1597,7 +1598,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv Shutdown (with signed htlcs)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // actual test begins @@ -1609,7 +1610,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv Shutdown (while waiting for a RevokeAndAck)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] @@ -1628,14 +1629,14 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { sender.send(bob, CMD_CLOSE(None)) bob2alice.expectMsgType[Shutdown] // this is just so we have something to sign - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) // now we can sign sender.send(alice, CMD_SIGN) sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] alice2bob.forward(bob) // adding an outgoing pending htlc - val (r1, htlc1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r1, htlc1) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) // actual test begins // alice eventually gets bob's shutdown bob2alice.forward(alice) @@ -1665,7 +1666,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CurrentBlockCount (no htlc timed out)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // actual test begins @@ -1677,7 +1678,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CurrentBlockCount (an htlc timed out)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // actual test begins @@ -1696,7 +1697,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CurrentBlockCount (fulfilled signed htlc ignored by upstream peer)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) val listener = TestProbe() @@ -1731,7 +1732,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CurrentBlockCount (fulfilled proposed htlc ignored by upstream peer)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) val listener = TestProbe() @@ -1766,7 +1767,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CurrentBlockCount (fulfilled proposed htlc acked but not committed by upstream peer)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) val listener = TestProbe() @@ -1844,11 +1845,11 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - val (ra1, htlca1) = addHtlc(250000000, alice, bob, alice2bob, bob2alice) - val (ra2, htlca2) = addHtlc(100000000, alice, bob, alice2bob, bob2alice) - val (ra3, htlca3) = addHtlc(10000, alice, bob, alice2bob, bob2alice) - val (rb1, htlcb1) = addHtlc(50000000, bob, alice, bob2alice, alice2bob) - val (rb2, htlcb2) = addHtlc(55000000, bob, alice, bob2alice, alice2bob) + val (ra1, htlca1) = addHtlc(MilliSatoshi(250000000), alice, bob, alice2bob, bob2alice) + val (ra2, htlca2) = addHtlc(MilliSatoshi(100000000), alice, bob, alice2bob, bob2alice) + val (ra3, htlca3) = addHtlc(MilliSatoshi(10000), alice, bob, alice2bob, bob2alice) + val (rb1, htlcb1) = addHtlc(MilliSatoshi(50000000), bob, alice, bob2alice, alice2bob) + val (rb2, htlcb2) = addHtlc(MilliSatoshi(55000000), bob, alice, bob2alice, alice2bob) crossSign(alice, bob, alice2bob, bob2alice) fulfillHtlc(1, ra2, bob, alice, bob2alice, alice2bob) fulfillHtlc(0, rb1, alice, bob, alice2bob, bob2alice) @@ -1896,8 +1897,8 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // assert the feerate of the claim main is what we expect val expectedFeeRate = alice.feeEstimator.getFeeratePerKw(alice.feeTargets.claimMainBlockTarget) - val expectedFee = Transactions.weight2fee(expectedFeeRate, Transactions.claimP2WPKHOutputWeight).toLong - val claimFee = claimMain.txIn.map(in => bobCommitTx.txOut(in.outPoint.index.toInt).amount.toLong).sum - claimMain.txOut.map(_.amount.toLong).sum + val expectedFee = Transactions.weight2fee(expectedFeeRate, Transactions.claimP2WPKHOutputWeight) + val claimFee = claimMain.txIn.map(in => bobCommitTx.txOut(in.outPoint.index.toInt).amount).sum - claimMain.txOut.map(_.amount).sum assert(claimFee == expectedFee) } @@ -1905,11 +1906,11 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - val (ra1, htlca1) = addHtlc(250000000, alice, bob, alice2bob, bob2alice) - val (ra2, htlca2) = addHtlc(100000000, alice, bob, alice2bob, bob2alice) - val (ra3, htlca3) = addHtlc(10000, alice, bob, alice2bob, bob2alice) - val (rb1, htlcb1) = addHtlc(50000000, bob, alice, bob2alice, alice2bob) - val (rb2, htlcb2) = addHtlc(55000000, bob, alice, bob2alice, alice2bob) + val (ra1, htlca1) = addHtlc(MilliSatoshi(250000000), alice, bob, alice2bob, bob2alice) + val (ra2, htlca2) = addHtlc(MilliSatoshi(100000000), alice, bob, alice2bob, bob2alice) + val (ra3, htlca3) = addHtlc(MilliSatoshi(10000), alice, bob, alice2bob, bob2alice) + val (rb1, htlcb1) = addHtlc(MilliSatoshi(50000000), bob, alice, bob2alice, alice2bob) + val (rb2, htlcb2) = addHtlc(MilliSatoshi(55000000), bob, alice, bob2alice, alice2bob) crossSign(alice, bob, alice2bob, bob2alice) fulfillHtlc(1, ra2, bob, alice, bob2alice, alice2bob) fulfillHtlc(0, rb1, alice, bob, alice2bob, bob2alice) @@ -1971,7 +1972,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // bob = 200 000 def send(): Transaction = { // alice sends 8 000 sat - val (r, htlc) = addHtlc(10000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(10000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx @@ -2030,7 +2031,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // alice = 800 000 // bob = 200 000 - val add = CMD_ADD_HTLC(10000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(10000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] @@ -2078,11 +2079,11 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv Error") { f => import f._ - val (ra1, htlca1) = addHtlc(250000000, alice, bob, alice2bob, bob2alice) - val (ra2, htlca2) = addHtlc(100000000, alice, bob, alice2bob, bob2alice) - val (ra3, htlca3) = addHtlc(10000, alice, bob, alice2bob, bob2alice) - val (rb1, htlcb1) = addHtlc(50000000, bob, alice, bob2alice, alice2bob) - val (rb2, htlcb2) = addHtlc(55000000, bob, alice, bob2alice, alice2bob) + val (ra1, htlca1) = addHtlc(MilliSatoshi(250000000), alice, bob, alice2bob, bob2alice) + val (ra2, htlca2) = addHtlc(MilliSatoshi(100000000), alice, bob, alice2bob, bob2alice) + val (ra3, htlca3) = addHtlc(MilliSatoshi(10000), alice, bob, alice2bob, bob2alice) + val (rb1, htlcb1) = addHtlc(MilliSatoshi(50000000), bob, alice, bob2alice, alice2bob) + val (rb2, htlcb2) = addHtlc(MilliSatoshi(55000000), bob, alice, bob2alice, alice2bob) crossSign(alice, bob, alice2bob, bob2alice) fulfillHtlc(1, ra2, bob, alice, bob2alice, alice2bob) fulfillHtlc(0, rb1, alice, bob, alice2bob, bob2alice) @@ -2299,8 +2300,8 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { sender.send(alice, WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null)) val update1a = alice2bob.expectMsgType[ChannelUpdate] assert(Announcements.isEnabled(update1a.channelFlags) == true) - val (_, htlc1) = addHtlc(10000, alice, bob, alice2bob, bob2alice) - val (_, htlc2) = addHtlc(10000, alice, bob, alice2bob, bob2alice) + val (_, htlc1) = addHtlc(MilliSatoshi(10000), alice, bob, alice2bob, bob2alice) + val (_, htlc2) = addHtlc(MilliSatoshi(10000), alice, bob, alice2bob, bob2alice) val aliceData = alice.stateData.asInstanceOf[DATA_NORMAL] assert(aliceData.commitments.localChanges.proposed.size == 2) @@ -2343,8 +2344,8 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val update1a = channelUpdateListener.expectMsgType[LocalChannelUpdate] val update1b = channelUpdateListener.expectMsgType[LocalChannelUpdate] assert(Announcements.isEnabled(update1a.channelUpdate.channelFlags) == true) - val (_, htlc1) = addHtlc(10000, alice, bob, alice2bob, bob2alice) - val (_, htlc2) = addHtlc(10000, alice, bob, alice2bob, bob2alice) + val (_, htlc1) = addHtlc(MilliSatoshi(10000), alice, bob, alice2bob, bob2alice) + val (_, htlc2) = addHtlc(MilliSatoshi(10000), alice, bob, alice2bob, bob2alice) val aliceData = alice.stateData.asInstanceOf[DATA_NORMAL] assert(aliceData.commitments.localChanges.proposed.size == 2) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/OfflineStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/OfflineStateSpec.scala index 866a3346f6..916b54c184 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/OfflineStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/OfflineStateSpec.scala @@ -31,7 +31,7 @@ import fr.acinq.eclair.payment.CommandBuffer.CommandSend import fr.acinq.eclair.router.Announcements import fr.acinq.eclair.transactions.Transactions.HtlcSuccessTx import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{TestConstants, TestkitBaseClass, randomBytes32} +import fr.acinq.eclair.{MilliSatoshi, TestConstants, TestkitBaseClass, randomBytes32} import org.scalatest.Outcome import scala.concurrent.duration._ @@ -66,7 +66,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - sender.send(alice, CMD_ADD_HTLC(1000000, ByteVector32.Zeroes, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(1000000), ByteVector32.Zeroes, 400144, 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, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(1000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) val ab_add_0 = alice2bob.expectMsgType[UpdateAddHtlc] // add ->b alice2bob.forward(bob, ab_add_0) @@ -204,11 +204,11 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - val (ra1, htlca1) = addHtlc(250000000, alice, bob, alice2bob, bob2alice) + val (ra1, htlca1) = addHtlc(MilliSatoshi(250000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) - val (ra2, htlca2) = addHtlc(100000000, alice, bob, alice2bob, bob2alice) + val (ra2, htlca2) = addHtlc(MilliSatoshi(100000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) - val (ra3, htlca3) = addHtlc(10000, alice, bob, alice2bob, bob2alice) + val (ra3, htlca3) = addHtlc(MilliSatoshi(10000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) val oldStateData = alice.stateData fulfillHtlc(htlca1.id, ra1, bob, alice, bob2alice, alice2bob) @@ -261,7 +261,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // we start by storing the current state val oldStateData = alice.stateData // then we add an htlc and sign it - addHtlc(250000000, alice, bob, alice2bob, bob2alice) + addHtlc(MilliSatoshi(250000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] @@ -347,7 +347,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { channelUpdateListener.expectNoMsg(300 millis) // we make alice update here relay fee - sender.send(alice, CMD_UPDATE_RELAY_FEE(4200, 123456)) + sender.send(alice, CMD_UPDATE_RELAY_FEE(MilliSatoshi(4200), 123456)) sender.expectMsg("ok") // alice doesn't broadcast the new channel_update yet @@ -365,7 +365,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // then alice reaches NORMAL state, and after a delay she broadcasts the channel_update val channelUpdate = channelUpdateListener.expectMsgType[LocalChannelUpdate](20 seconds).channelUpdate - assert(channelUpdate.feeBaseMsat === 4200) + assert(channelUpdate.feeBaseMsat === MilliSatoshi(4200)) assert(channelUpdate.feeProportionalMillionths === 123456) assert(Announcements.isEnabled(channelUpdate.channelFlags)) @@ -387,7 +387,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { channelUpdateListener.expectNoMsg(300 millis) // we attempt to send a payment - sender.send(alice, CMD_ADD_HTLC(4200, randomBytes32, 123456, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(4200), randomBytes32, 123456, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) val failure = sender.expectMsgType[Status.Failure] val AddHtlcFailed(_, _, ChannelUnavailable(_), _, _, _) = failure.cause @@ -401,7 +401,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val sender = TestProbe() val register = TestProbe() val commandBuffer = TestActorRef(new CommandBuffer(bob.underlyingActor.nodeParams, register.ref)) - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) val listener = TestProbe() @@ -442,7 +442,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val sender = TestProbe() val register = TestProbe() val commandBuffer = TestActorRef(new CommandBuffer(bob.underlyingActor.nodeParams, register.ref)) - val (_, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (_, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) sender.send(alice, INPUT_DISCONNECTED) @@ -452,7 +452,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // We simulate a pending failure on that HTLC. // Even if we get close to expiring upstream we shouldn't close the channel, because we have nothing to lose. - sender.send(commandBuffer, CommandSend(htlc.channelId, htlc.id, CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(0))))) + sender.send(commandBuffer, CommandSend(htlc.channelId, htlc.id, CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(MilliSatoshi(0)))))) sender.send(bob, CurrentBlockCount(htlc.cltvExpiry - bob.underlyingActor.nodeParams.fulfillSafetyBeforeTimeoutBlocks)) bob2blockchain.expectNoMsg(250 millis) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala index 760efbe3f6..0ca1582caa 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala @@ -30,7 +30,7 @@ import fr.acinq.eclair.channel.states.StateTestsHelperMethods import fr.acinq.eclair.payment._ import fr.acinq.eclair.router.Hop import fr.acinq.eclair.wire.{CommitSig, Error, FailureMessageCodecs, PermanentChannelFailure, RevokeAndAck, Shutdown, UpdateAddHtlc, UpdateFailHtlc, UpdateFailMalformedHtlc, UpdateFee, UpdateFulfillHtlc} -import fr.acinq.eclair.{Globals, TestConstants, TestkitBaseClass, randomBytes32} +import fr.acinq.eclair.{Globals, MilliSatoshi, TestConstants, TestkitBaseClass, randomBytes32} import org.scalatest.{Outcome, Tag} import scodec.bits.ByteVector @@ -55,7 +55,7 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val sender = TestProbe() // alice sends an HTLC to bob val h1 = Crypto.sha256(r1) - val amount1 = 300000000 + val amount1 = MilliSatoshi(300000000) val expiry1 = 400144 val cmd1 = PaymentLifecycle.buildCommand(UUID.randomUUID, amount1, expiry1, h1, Hop(null, TestConstants.Bob.nodeParams.nodeId, null) :: Nil)._1.copy(commit = false) sender.send(alice, cmd1) @@ -65,7 +65,7 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods { awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.remoteChanges.proposed == htlc1 :: Nil) // alice sends another HTLC to bob val h2 = Crypto.sha256(r2) - val amount2 = 200000000 + val amount2 = MilliSatoshi(200000000) val expiry2 = 400144 val cmd2 = PaymentLifecycle.buildCommand(UUID.randomUUID, amount2, expiry2, h2, Hop(null, TestConstants.Bob.nodeParams.nodeId, null) :: Nil)._1.copy(commit = false) sender.send(alice, cmd2) @@ -104,7 +104,7 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_ADD_HTLC") { f => import f._ val sender = TestProbe() - val add = CMD_ADD_HTLC(500000000, r1, cltvExpiry = 300000, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(500000000), r1, cltvExpiry = 300000, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = ChannelUnavailable(channelId(alice)) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), None, Some(add)))) @@ -585,7 +585,7 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods { bob.feeEstimator.setFeerate(FeeratesPerKw.single(fee.feeratePerKw)) sender.send(bob, fee) val error = bob2alice.expectMsgType[Error] - assert(new String(error.data.toArray) === CannotAffordFees(channelId(bob), missingSatoshis = 72120000L, reserveSatoshis = 20000L, feesSatoshis = 72400000L).getMessage) + assert(new String(error.data.toArray) === CannotAffordFees(channelId(bob), missing = Satoshi(72120000L), reserve = Satoshi(20000L), fees = Satoshi(72400000L)).getMessage) awaitCond(bob.stateName == CLOSING) bob2blockchain.expectMsg(PublishAsap(tx)) // commit tx //bob2blockchain.expectMsgType[PublishAsap] // main delayed (removed because of the high fees) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/g/NegotiatingStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/g/NegotiatingStateSpec.scala index 3b798fe887..f5c5a59cd4 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/g/NegotiatingStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/g/NegotiatingStateSpec.scala @@ -30,7 +30,7 @@ import fr.acinq.eclair.channel._ import fr.acinq.eclair.channel.states.StateTestsHelperMethods import fr.acinq.eclair.payment.Local import fr.acinq.eclair.wire.{ClosingSigned, Error, Shutdown} -import fr.acinq.eclair.{Globals, TestConstants, TestkitBaseClass} +import fr.acinq.eclair.{Globals, MilliSatoshi, TestConstants, TestkitBaseClass} import org.scalatest.{Outcome, Tag} import scodec.bits.ByteVector @@ -85,7 +85,7 @@ class NegotiatingStateSpec extends TestkitBaseClass with StateTestsHelperMethods import f._ alice2bob.expectMsgType[ClosingSigned] val sender = TestProbe() - val add = CMD_ADD_HTLC(500000000, ByteVector32(ByteVector.fill(32)(1)), cltvExpiry = 300000, onion = TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(5000000000L), ByteVector32(ByteVector.fill(32)(1)), cltvExpiry = 300000, onion = TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = ChannelUnavailable(channelId(alice)) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), None, Some(add)))) @@ -111,7 +111,7 @@ class NegotiatingStateSpec extends TestkitBaseClass with StateTestsHelperMethods private def testFeeConverge(f: FixtureParam) = { import f._ - var aliceCloseFee, bobCloseFee = 0L + var aliceCloseFee, bobCloseFee = Satoshi(0) do { aliceCloseFee = alice2bob.expectMsgType[ClosingSigned].feeSatoshis alice2bob.forward(bob) @@ -135,9 +135,9 @@ class NegotiatingStateSpec extends TestkitBaseClass with StateTestsHelperMethods val aliceCloseSig = alice2bob.expectMsgType[ClosingSigned] val sender = TestProbe() val tx = bob.stateData.asInstanceOf[DATA_NEGOTIATING].commitments.localCommit.publishableTxs.commitTx.tx - sender.send(bob, aliceCloseSig.copy(feeSatoshis = 99000)) // sig doesn't matter, it is checked later + sender.send(bob, aliceCloseSig.copy(feeSatoshis = Satoshi(99000))) // sig doesn't matter, it is checked later val error = bob2alice.expectMsgType[Error] - assert(new String(error.data.toArray).startsWith("invalid close fee: fee_satoshis=99000")) + assert(new String(error.data.toArray).startsWith("invalid close fee: fee_satoshis=Satoshi(99000)")) bob2blockchain.expectMsg(PublishAsap(tx)) bob2blockchain.expectMsgType[PublishAsap] bob2blockchain.expectMsgType[WatchConfirmed] @@ -158,7 +158,7 @@ class NegotiatingStateSpec extends TestkitBaseClass with StateTestsHelperMethods test("recv BITCOIN_FUNDING_SPENT (counterparty's mutual close)") { f => import f._ - var aliceCloseFee, bobCloseFee = 0L + var aliceCloseFee, bobCloseFee = Satoshi(0) do { aliceCloseFee = alice2bob.expectMsgType[ClosingSigned].feeSatoshis alice2bob.forward(bob) @@ -193,7 +193,7 @@ class NegotiatingStateSpec extends TestkitBaseClass with StateTestsHelperMethods // at this point alice and bob have not yet converged on closing fees, but bob decides to publish a mutual close with one of the previous sigs val d = bob.stateData.asInstanceOf[DATA_NEGOTIATING] implicit val log: LoggingAdapter = bob.underlyingActor.implicitLog - val Success(bobClosingTx) = Closing.checkClosingSignature(Bob.keyManager, d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, Satoshi(aliceClose1.feeSatoshis), aliceClose1.signature) + val Success(bobClosingTx) = Closing.checkClosingSignature(Bob.keyManager, d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, aliceClose1.feeSatoshis, aliceClose1.signature) alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobClosingTx) alice2blockchain.expectMsgType[PublishAsap] diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala index b4715a7110..8cff5d868f 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala @@ -22,7 +22,7 @@ import akka.actor.Status import akka.actor.Status.Failure import akka.testkit.{TestFSMRef, TestProbe} import com.typesafe.sslconfig.util.NoopLogger -import fr.acinq.bitcoin.{ByteVector32, OutPoint, ScriptFlags, Transaction, TxIn} +import fr.acinq.bitcoin.{ByteVector32, OutPoint, Satoshi, ScriptFlags, Transaction, TxIn} import fr.acinq.eclair.TestConstants.{Alice, Bob, TestFeeEstimator} import fr.acinq.eclair.blockchain._ import fr.acinq.eclair.blockchain.fee.FeeratesPerKw @@ -33,7 +33,7 @@ import fr.acinq.eclair.channel.{Data, State, _} import fr.acinq.eclair.payment._ import fr.acinq.eclair.transactions.{Scripts, Transactions} import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{Globals, TestConstants, TestkitBaseClass, randomBytes32} +import fr.acinq.eclair.{Globals, MilliSatoshi, TestConstants, TestkitBaseClass, randomBytes32} import org.scalatest.{Outcome, Tag} import scodec.bits.ByteVector @@ -89,7 +89,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { within(30 seconds) { reachNormal(setup) val bobCommitTxes: List[PublishableTxs] = (for (amt <- List(100000000, 200000000, 300000000)) yield { - val (r, htlc) = addHtlc(amt, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(amt), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) relayerB.expectMsgType[ForwardAdd] val bobCommitTx1 = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs @@ -124,7 +124,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { bob2alice.expectMsgType[Shutdown] bob2alice.forward(alice) // agreeing on a closing fee - var aliceCloseFee, bobCloseFee = 0L + var aliceCloseFee, bobCloseFee = Satoshi(0) do { aliceCloseFee = alice2bob.expectMsgType[ClosingSigned].feeSatoshis alice2bob.forward(bob) @@ -157,7 +157,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val mutualClosingFeeRate = alice.feeEstimator.getFeeratePerKw(alice.feeTargets.mutualCloseBlockTarget) val expectedFirstProposedFee = Closing.firstClosingFee(aliceData.commitments, aliceData.localShutdown.scriptPubKey, aliceData.remoteShutdown.scriptPubKey, mutualClosingFeeRate)(akka.event.NoLogging) assert(alice.feeTargets.mutualCloseBlockTarget == 2 && mutualClosingFeeRate == 250) - assert(closing.feeSatoshis == expectedFirstProposedFee.amount) + assert(closing.feeSatoshis == expectedFirstProposedFee) } test("recv BITCOIN_FUNDING_PUBLISH_FAILED", Tag("funding_unconfirmed")) { f => @@ -301,7 +301,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // actual test starts here val sender = TestProbe() - val add = CMD_ADD_HTLC(500000000, ByteVector32(ByteVector.fill(32)(1)), cltvExpiry = 300000, onion = TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(500000000), ByteVector32(ByteVector.fill(32)(1)), cltvExpiry = 300000, onion = TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = ChannelUnavailable(channelId(alice)) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), None, Some(add)))) @@ -380,7 +380,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv BITCOIN_OUTPUT_SPENT") { f => import f._ // alice sends an htlc to bob - val (ra1, htlca1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (ra1, htlca1) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) relayerB.expectMsgType[ForwardAdd] // an error occurs and alice publishes her commit tx @@ -420,7 +420,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { system.eventStream.subscribe(listener.ref, classOf[LocalCommitConfirmed]) system.eventStream.subscribe(listener.ref, classOf[PaymentSettlingOnChain]) // alice sends an htlc to bob - val (ra1, htlca1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (ra1, htlca1) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // an error occurs and alice publishes her commit tx val aliceCommitTx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx @@ -454,7 +454,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { system.eventStream.subscribe(listener.ref, classOf[PaymentSettlingOnChain]) val aliceCommitTx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx // alice sends an htlc - val (r, htlc) = addHtlc(4200000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(4200000), alice, bob, alice2bob, bob2alice) // and signs it (but bob doesn't sign it) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -485,7 +485,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { system.eventStream.subscribe(listener.ref, classOf[PaymentSettlingOnChain]) val bobCommitTx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx // alice sends an htlc - val (r, htlc) = addHtlc(4200000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(4200000), alice, bob, alice2bob, bob2alice) // and signs it (but bob doesn't sign it) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -546,7 +546,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val oldStateData = alice.stateData - val (ra1, htlca1) = addHtlc(25000000, alice, bob, alice2bob, bob2alice) + val (ra1, htlca1) = addHtlc(MilliSatoshi(25000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) fulfillHtlc(htlca1.id, ra1, bob, alice, bob2alice, alice2bob) crossSign(bob, alice, bob2alice, alice2bob) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteAuditDbSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteAuditDbSpec.scala index 282841c3cc..8892bf3adc 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteAuditDbSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteAuditDbSpec.scala @@ -18,14 +18,14 @@ package fr.acinq.eclair.db import java.util.UUID -import fr.acinq.bitcoin.{MilliSatoshi, Satoshi, Transaction} +import fr.acinq.bitcoin.{Satoshi, Transaction} import fr.acinq.eclair.channel.Channel.{LocalError, RemoteError} import fr.acinq.eclair.channel.{AvailableBalanceChanged, ChannelErrorOccured, NetworkFeePaid} import fr.acinq.eclair.db.sqlite.SqliteAuditDb import fr.acinq.eclair.db.sqlite.SqliteUtils.{getVersion, using} -import fr.acinq.eclair.payment.{PaymentReceived, PaymentRelayed, PaymentSent} import fr.acinq.eclair.wire.{ChannelCodecs, ChannelCodecsSpec} import fr.acinq.eclair._ +import fr.acinq.eclair.payment.{PaymentReceived, PaymentRelayed, PaymentSent} import org.scalatest.FunSuite import concurrent.duration._ @@ -50,8 +50,8 @@ class SqliteAuditDbSpec extends FunSuite { val e4 = NetworkFeePaid(null, randomKey.publicKey, randomBytes32, Transaction(0, Seq.empty, Seq.empty, 0), Satoshi(42), "mutual") val e5 = PaymentSent(ChannelCodecs.UNKNOWN_UUID, MilliSatoshi(42000), MilliSatoshi(1000), randomBytes32, randomBytes32, randomBytes32, timestamp = 0) val e6 = PaymentSent(ChannelCodecs.UNKNOWN_UUID, MilliSatoshi(42000), MilliSatoshi(1000), randomBytes32, randomBytes32, randomBytes32, timestamp = (Platform.currentTime.milliseconds + 10.minutes).toMillis) - val e7 = AvailableBalanceChanged(null, randomBytes32, ShortChannelId(500000, 42, 1), 456123000, ChannelCodecsSpec.commitments) - val e8 = ChannelLifecycleEvent(randomBytes32, randomKey.publicKey, 456123000, true, false, "mutual") + val e7 = AvailableBalanceChanged(null, randomBytes32, ShortChannelId(500000, 42, 1), MilliSatoshi(456123000), ChannelCodecsSpec.commitments) + val e8 = ChannelLifecycleEvent(randomBytes32, randomKey.publicKey, Satoshi(456123000), true, false, "mutual") val e9 = ChannelErrorOccured(null, randomBytes32, randomKey.publicKey, null, LocalError(new RuntimeException("oops")), true) val e10 = ChannelErrorOccured(null, randomBytes32, randomKey.publicKey, null, RemoteError(wire.Error(randomBytes32, "remote oops")), true) @@ -97,9 +97,9 @@ class SqliteAuditDbSpec extends FunSuite { db.add(NetworkFeePaid(null, n3, c3, Transaction(0, Seq.empty, Seq.empty, 0), Satoshi(400), "funding")) assert(db.stats.toSet === Set( - Stats(channelId = c1, avgPaymentAmountSatoshi = 42, paymentCount = 3, relayFeeSatoshi = 4, networkFeeSatoshi = 100), - Stats(channelId = c2, avgPaymentAmountSatoshi = 40, paymentCount = 1, relayFeeSatoshi = 2, networkFeeSatoshi = 500), - Stats(channelId = c3, avgPaymentAmountSatoshi = 0, paymentCount = 0, relayFeeSatoshi = 0, networkFeeSatoshi = 400) + Stats(channelId = c1, avgPaymentAmount = Satoshi(42), paymentCount = 3, relayFee = Satoshi(4), networkFee = Satoshi(100)), + Stats(channelId = c2, avgPaymentAmount = Satoshi(40), paymentCount = 1, relayFee = Satoshi(2), networkFee = Satoshi(500)), + Stats(channelId = c3, avgPaymentAmount = Satoshi(0), paymentCount = 0, relayFee = Satoshi(0), networkFee = Satoshi(400)) )) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteNetworkDbSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteNetworkDbSpec.scala index 201cff789a..a721c4d557 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteNetworkDbSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteNetworkDbSpec.scala @@ -20,7 +20,7 @@ import fr.acinq.bitcoin.{Block, Crypto, Satoshi} import fr.acinq.eclair.db.sqlite.SqliteNetworkDb import fr.acinq.eclair.router.Announcements import fr.acinq.eclair.wire.{Color, NodeAddress, Tor2} -import fr.acinq.eclair.{ShortChannelId, TestConstants, randomBytes32, randomKey} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, TestConstants, randomBytes32, randomKey} import org.scalatest.FunSuite import org.sqlite.SQLiteException @@ -85,9 +85,9 @@ class SqliteNetworkDbSpec extends FunSuite { db.removeChannel(channel_2.shortChannelId) assert(db.listChannels().toSet === Set((channel_1, (txid_1, capacity)), (channel_3, (txid_3, capacity)))) - val channel_update_1 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, ShortChannelId(42), 5, 7000000, 50000, 100, 500000000L, true) - val channel_update_2 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, ShortChannelId(43), 5, 7000000, 50000, 100, 500000000L, true) - val channel_update_3 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, ShortChannelId(44), 5, 7000000, 50000, 100, 500000000L, true) + val channel_update_1 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, ShortChannelId(42), 5, MilliSatoshi(7000000), MilliSatoshi(50000), 100, MilliSatoshi(500000000L), true) + val channel_update_2 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, ShortChannelId(43), 5, MilliSatoshi(7000000), MilliSatoshi(50000), 100, MilliSatoshi(500000000L), true) + val channel_update_3 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, ShortChannelId(44), 5, MilliSatoshi(7000000), MilliSatoshi(50000), 100, MilliSatoshi(500000000L), true) assert(db.listChannelUpdates().toSet === Set.empty) db.addChannelUpdate(channel_update_1) @@ -110,7 +110,7 @@ class SqliteNetworkDbSpec extends FunSuite { val capacity = Satoshi(10000) val channels = shortChannelIds.map(id => Announcements.makeChannelAnnouncement(Block.RegtestGenesisBlock.hash, id, pub, pub, pub, pub, sig, sig, sig, sig)) - val template = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv, pub, ShortChannelId(42), 5, 7000000, 50000, 100, 500000000L, true) + val template = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv, pub, ShortChannelId(42), 5, MilliSatoshi(7000000), MilliSatoshi(50000), 100, MilliSatoshi(500000000L), true) val updates = shortChannelIds.map(id => template.copy(shortChannelId = id)) val txid = randomBytes32 channels.foreach(ca => db.addChannel(ca, txid, capacity)) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/db/SqlitePaymentsDbSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/db/SqlitePaymentsDbSpec.scala index b8eec4b784..86abf94cf0 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/db/SqlitePaymentsDbSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/db/SqlitePaymentsDbSpec.scala @@ -17,17 +17,19 @@ package fr.acinq.eclair.db import java.util.UUID + import fr.acinq.eclair.db.sqlite.SqliteUtils._ -import fr.acinq.bitcoin.{Block, ByteVector32, MilliSatoshi} +import fr.acinq.bitcoin.{Block, ByteVector32} import fr.acinq.eclair.TestConstants.Bob -import fr.acinq.eclair.{TestConstants, payment} +import fr.acinq.eclair.{MilliSatoshi, TestConstants, payment, randomBytes32} import fr.acinq.eclair.db.sqlite.SqlitePaymentsDb import fr.acinq.eclair.payment.PaymentRequest import org.scalatest.FunSuite import scodec.bits._ -import fr.acinq.eclair.randomBytes32 + import scala.compat.Platform import OutgoingPaymentStatus._ + import concurrent.duration._ class SqlitePaymentsDbSpec extends FunSuite { @@ -51,12 +53,12 @@ class SqlitePaymentsDbSpec extends FunSuite { assert(getVersion(statement, "payments", 1) == 1) // version 1 is deployed now } - val oldReceivedPayment = IncomingPayment(ByteVector32(hex"0f059ef9b55bb70cc09069ee4df854bf0fab650eee6f2b87ba26d1ad08ab114f"), 123, 1233322) + val oldReceivedPayment = IncomingPayment(ByteVector32(hex"0f059ef9b55bb70cc09069ee4df854bf0fab650eee6f2b87ba26d1ad08ab114f"), MilliSatoshi(123), 1233322) // insert old type record using(connection.prepareStatement("INSERT INTO payments VALUES (?, ?, ?)")) { statement => statement.setBytes(1, oldReceivedPayment.paymentHash.toArray) - statement.setLong(2, oldReceivedPayment.amountMsat) + statement.setLong(2, oldReceivedPayment.amount.toLong) statement.setLong(3, oldReceivedPayment.receivedAt) statement.executeUpdate() } @@ -71,9 +73,9 @@ class SqlitePaymentsDbSpec extends FunSuite { assert(preMigrationDb.getIncomingPayment(oldReceivedPayment.paymentHash).isEmpty) // add a few rows - val ps1 = OutgoingPayment(id = UUID.randomUUID(), paymentHash = ByteVector32(hex"0f059ef9b55bb70cc09069ee4df854bf0fab650eee6f2b87ba26d1ad08ab114f"), None, amountMsat = 12345, createdAt = 12345, None, PENDING) + val ps1 = OutgoingPayment(id = UUID.randomUUID(), paymentHash = ByteVector32(hex"0f059ef9b55bb70cc09069ee4df854bf0fab650eee6f2b87ba26d1ad08ab114f"), None, amount = MilliSatoshi(12345), createdAt = 12345, None, PENDING) val i1 = PaymentRequest.read("lnbc10u1pw2t4phpp5ezwm2gdccydhnphfyepklc0wjkxhz0r4tctg9paunh2lxgeqhcmsdqlxycrqvpqwdshgueqvfjhggr0dcsry7qcqzpgfa4ecv7447p9t5hkujy9qgrxvkkf396p9zar9p87rv2htmeuunkhydl40r64n5s2k0u7uelzc8twxmp37nkcch6m0wg5tvvx69yjz8qpk94qf3") - val pr1 = IncomingPayment(i1.paymentHash, 12345678, 1513871928275L) + val pr1 = IncomingPayment(i1.paymentHash, MilliSatoshi(12345678), 1513871928275L) preMigrationDb.addPaymentRequest(i1, ByteVector32.Zeroes) preMigrationDb.addIncomingPayment(pr1) @@ -99,7 +101,7 @@ class SqlitePaymentsDbSpec extends FunSuite { val db = new SqlitePaymentsDb(sqlite) // can't receive a payment without an invoice associated with it - assertThrows[IllegalArgumentException](db.addIncomingPayment(IncomingPayment(ByteVector32(hex"6e7e8018f05e169cf1d99e77dc22cb372d09f10b6a81f1eae410718c56cad188"), 12345678, 1513871928275L))) + assertThrows[IllegalArgumentException](db.addIncomingPayment(IncomingPayment(ByteVector32(hex"6e7e8018f05e169cf1d99e77dc22cb372d09f10b6a81f1eae410718c56cad188"), MilliSatoshi(12345678), 1513871928275L))) val i1 = PaymentRequest.read("lnbc5450n1pw2t4qdpp5vcrf6ylgpettyng4ac3vujsk0zpc25cj0q3zp7l7w44zvxmpzh8qdzz2pshjmt9de6zqen0wgsr2dp4ypcxj7r9d3ejqct5ypekzar0wd5xjuewwpkxzcm99cxqzjccqp2rzjqtspxelp67qc5l56p6999wkatsexzhs826xmupyhk6j8lxl038t27z9tsqqqgpgqqqqqqqlgqqqqqzsqpcz8z8hmy8g3ecunle4n3edn3zg2rly8g4klsk5md736vaqqy3ktxs30ht34rkfkqaffzxmjphvd0637dk2lp6skah2hq09z6lrjna3xqp3d4vyd") val i2 = PaymentRequest.read("lnbc10u1pw2t4phpp5ezwm2gdccydhnphfyepklc0wjkxhz0r4tctg9paunh2lxgeqhcmsdqlxycrqvpqwdshgueqvfjhggr0dcsry7qcqzpgfa4ecv7447p9t5hkujy9qgrxvkkf396p9zar9p87rv2htmeuunkhydl40r64n5s2k0u7uelzc8twxmp37nkcch6m0wg5tvvx69yjz8qpk94qf3") @@ -107,8 +109,8 @@ class SqlitePaymentsDbSpec extends FunSuite { db.addPaymentRequest(i1, ByteVector32.Zeroes) db.addPaymentRequest(i2, ByteVector32.Zeroes) - val p1 = IncomingPayment(i1.paymentHash, 12345678, 1513871928275L) - val p2 = IncomingPayment(i2.paymentHash, 12345678, 1513871928275L) + val p1 = IncomingPayment(i1.paymentHash, MilliSatoshi(12345678), 1513871928275L) + val p2 = IncomingPayment(i2.paymentHash, MilliSatoshi(12345678), 1513871928275L) assert(db.listIncomingPayments() === Nil) db.addIncomingPayment(p1) db.addIncomingPayment(p2) @@ -121,8 +123,8 @@ class SqlitePaymentsDbSpec extends FunSuite { val db = new SqlitePaymentsDb(TestConstants.sqliteInMemory()) - val s1 = OutgoingPayment(id = UUID.randomUUID(), paymentHash = ByteVector32(hex"0f059ef9b55bb70cc09069ee4df854bf0fab650eee6f2b87ba26d1ad08ab114f"), None, amountMsat = 12345, createdAt = 12345, None, PENDING) - val s2 = OutgoingPayment(id = UUID.randomUUID(), paymentHash = ByteVector32(hex"08d47d5f7164d4b696e8f6b62a03094d4f1c65f16e9d7b11c4a98854707e55cf"), None, amountMsat = 12345, createdAt = 12345, None, PENDING) + val s1 = OutgoingPayment(id = UUID.randomUUID(), paymentHash = ByteVector32(hex"0f059ef9b55bb70cc09069ee4df854bf0fab650eee6f2b87ba26d1ad08ab114f"), None, amount = MilliSatoshi(12345), createdAt = 12345, None, PENDING) + val s2 = OutgoingPayment(id = UUID.randomUUID(), paymentHash = ByteVector32(hex"08d47d5f7164d4b696e8f6b62a03094d4f1c65f16e9d7b11c4a98854707e55cf"), None, amount = MilliSatoshi(12345), createdAt = 12345, None, PENDING) assert(db.listOutgoingPayments().isEmpty) db.addOutgoingPayment(s1) @@ -135,7 +137,7 @@ class SqlitePaymentsDbSpec extends FunSuite { assert(db.getOutgoingPayments(s2.paymentHash) === Seq(s2)) assert(db.getOutgoingPayments(ByteVector32.Zeroes) === Seq.empty) - val s3 = s2.copy(id = UUID.randomUUID(), amountMsat = 88776655) + val s3 = s2.copy(id = UUID.randomUUID(), amount = MilliSatoshi(88776655)) db.addOutgoingPayment(s3) db.updateOutgoingPayment(s3.id, FAILED) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/integration/IntegrationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/integration/IntegrationSpec.scala index 8b2c2e79da..0f9857d006 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/integration/IntegrationSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/integration/IntegrationSpec.scala @@ -24,13 +24,14 @@ import akka.testkit.{TestKit, TestProbe} import com.google.common.net.HostAndPort import com.typesafe.config.{Config, ConfigFactory} import fr.acinq.bitcoin.Crypto.PrivateKey -import fr.acinq.bitcoin.{Base58, Base58Check, Bech32, Block, ByteVector32, Crypto, MilliSatoshi, OP_0, OP_CHECKSIG, OP_DUP, OP_EQUAL, OP_EQUALVERIFY, OP_HASH160, OP_PUSHDATA, Satoshi, Script, ScriptFlags, Transaction} +import fr.acinq.bitcoin.{Base58, Base58Check, Bech32, Block, ByteVector32, Crypto, OP_0, OP_CHECKSIG, OP_DUP, OP_EQUAL, OP_EQUALVERIFY, OP_HASH160, OP_PUSHDATA, Satoshi, Script, ScriptFlags, Transaction} import fr.acinq.eclair.blockchain.bitcoind.BitcoindService import fr.acinq.eclair.blockchain.bitcoind.rpc.ExtendedBitcoinClient import fr.acinq.eclair.blockchain.{Watch, WatchConfirmed} import fr.acinq.eclair.channel.Channel.{BroadcastChannelUpdate, PeriodicRefresh} import fr.acinq.eclair.channel.Register.{Forward, ForwardShortId} import fr.acinq.eclair.channel._ +import fr.acinq.eclair._ import fr.acinq.eclair.crypto.Sphinx.DecryptedFailurePacket import fr.acinq.eclair.io.Peer.{Disconnect, PeerRoutingMessage} import fr.acinq.eclair.io.{NodeURI, Peer} @@ -65,7 +66,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService // we override the default because these test were designed to use cost-optimized routes val integrationTestRouteParams = Some(RouteParams( randomize = false, - maxFeeBaseMsat = Long.MaxValue, + maxFeeBase = MilliSatoshi(Long.MaxValue), maxFeePct = Double.MaxValue, routeMaxCltv = Int.MaxValue, routeMaxLength = ROUTE_MAX_LENGTH, @@ -263,7 +264,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val pr = sender.expectMsgType[PaymentRequest] // then we make the actual payment sender.send(nodes("A").paymentInitiator, - SendPayment(amountMsat.amount, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 1)) + SendPayment(amountMsat, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 1)) val paymentId = sender.expectMsgType[UUID](5 seconds) val ps = sender.expectMsgType[PaymentSucceeded](5 seconds) assert(ps.id == paymentId) @@ -279,7 +280,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService sender.send(nodes("B").register, ForwardShortId(shortIdBC, CMD_GETINFO)) val commitmentBC = sender.expectMsgType[RES_GETINFO].data.asInstanceOf[DATA_NORMAL].commitments // we then forge a new channel_update for B-C... - val channelUpdateBC = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, nodes("B").nodeParams.privateKey, nodes("C").nodeParams.nodeId, shortIdBC, nodes("B").nodeParams.expiryDeltaBlocks + 1, nodes("C").nodeParams.htlcMinimumMsat, nodes("B").nodeParams.feeBaseMsat, nodes("B").nodeParams.feeProportionalMillionth, 500000000L) + val channelUpdateBC = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, nodes("B").nodeParams.privateKey, nodes("C").nodeParams.nodeId, shortIdBC, nodes("B").nodeParams.expiryDeltaBlocks + 1, nodes("C").nodeParams.htlcMinimum, nodes("B").nodeParams.feeBase, nodes("B").nodeParams.feeProportionalMillionth, MilliSatoshi(500000000L)) // ...and notify B's relayer sender.send(nodes("B").relayer, LocalChannelUpdate(system.deadLetters, commitmentBC.channelId, shortIdBC, commitmentBC.remoteParams.nodeId, None, channelUpdateBC, commitmentBC)) // we retrieve a payment hash from D @@ -287,7 +288,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService sender.send(nodes("D").paymentHandler, ReceivePayment(Some(amountMsat), "1 coffee")) val pr = sender.expectMsgType[PaymentRequest] // then we make the actual payment, do not randomize the route to make sure we route through node B - val sendReq = SendPayment(amountMsat.amount, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPayment(amountMsat, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, sendReq) // A will receive an error from B that include the updated channel update, then will retry the payment val paymentId = sender.expectMsgType[UUID](5 seconds) @@ -325,7 +326,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService sender.send(nodes("D").paymentHandler, ReceivePayment(Some(amountMsat), "1 coffee")) val pr = sender.expectMsgType[PaymentRequest] // then we make the payment (B-C has a smaller capacity than A-B and C-D) - val sendReq = SendPayment(amountMsat.amount, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPayment(amountMsat, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, sendReq) // A will first receive an error from C, then retry and route around C: A->B->E->C->D sender.expectMsgType[UUID](5 seconds) @@ -334,7 +335,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService test("send an HTLC A->D with an unknown payment hash") { val sender = TestProbe() - val pr = SendPayment(100000000L, randomBytes32, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) + val pr = SendPayment(MilliSatoshi(100000000L), randomBytes32, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, pr) // A will receive an error from D and won't retry @@ -343,7 +344,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(100000000L))) + assert(failed.failures.head.asInstanceOf[RemoteFailure].e === DecryptedFailurePacket(nodes("D").nodeParams.nodeId, IncorrectOrUnknownPaymentDetails(MilliSatoshi(100000000L)))) } test("send an HTLC A->D with a lower amount than requested") { @@ -354,7 +355,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val pr = sender.expectMsgType[PaymentRequest] // A send payment of only 1 mBTC - val sendReq = SendPayment(100000000L, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPayment(MilliSatoshi(100000000L), pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, sendReq) // A will first receive an IncorrectPaymentAmount error from D @@ -363,7 +364,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(100000000L))) + assert(failed.failures.head.asInstanceOf[RemoteFailure].e === DecryptedFailurePacket(nodes("D").nodeParams.nodeId, IncorrectOrUnknownPaymentDetails(MilliSatoshi(100000000L)))) } test("send an HTLC A->D with too much overpayment") { @@ -374,7 +375,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val pr = sender.expectMsgType[PaymentRequest] // A send payment of 6 mBTC - val sendReq = SendPayment(600000000L, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPayment(MilliSatoshi(600000000L), pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, sendReq) // A will first receive an IncorrectPaymentAmount error from D @@ -383,7 +384,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(600000000L))) + assert(failed.failures.head.asInstanceOf[RemoteFailure].e === DecryptedFailurePacket(nodes("D").nodeParams.nodeId, IncorrectOrUnknownPaymentDetails(MilliSatoshi(600000000L)))) } test("send an HTLC A->D with a reasonable overpayment") { @@ -394,7 +395,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val pr = sender.expectMsgType[PaymentRequest] // A send payment of 3 mBTC, more than asked but it should still be accepted - val sendReq = SendPayment(300000000L, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPayment(MilliSatoshi(300000000L), pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, sendReq) sender.expectMsgType[UUID] } @@ -407,7 +408,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService sender.send(nodes("D").paymentHandler, ReceivePayment(Some(amountMsat), "1 payment")) val pr = sender.expectMsgType[PaymentRequest] - val sendReq = SendPayment(amountMsat.amount, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPayment(amountMsat, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, sendReq) sender.expectMsgType[UUID] sender.expectMsgType[PaymentSucceeded] // the payment FSM will also reply to the sender after the payment is completed @@ -423,7 +424,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService // the payment is requesting to use a capacity-optimized route which will select node G even though it's a bit more expensive sender.send(nodes("A").paymentInitiator, - SendPayment(amountMsat.amount, pr.paymentHash, nodes("C").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams.map(_.copy(ratios = Some(WeightRatios(0, 0, 1)))))) + SendPayment(amountMsat, pr.paymentHash, nodes("C").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams.map(_.copy(ratios = Some(WeightRatios(0, 0, 1)))))) sender.expectMsgType[UUID](max = 60 seconds) awaitCond({ @@ -467,7 +468,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val preimage = randomBytes32 val paymentHash = Crypto.sha256(preimage) // A sends a payment to F - val paymentReq = SendPayment(100000000L, paymentHash, nodes("F1").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams) + val paymentReq = SendPayment(MilliSatoshi(100000000L), paymentHash, nodes("F1").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams) val paymentSender = TestProbe() paymentSender.send(nodes("A").paymentInitiator, paymentReq) paymentSender.expectMsgType[UUID](30 seconds) @@ -547,7 +548,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val preimage = randomBytes32 val paymentHash = Crypto.sha256(preimage) // A sends a payment to F - val paymentReq = SendPayment(100000000L, paymentHash, nodes("F2").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams) + val paymentReq = SendPayment(MilliSatoshi(100000000L), paymentHash, nodes("F2").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams) val paymentSender = TestProbe() paymentSender.send(nodes("A").paymentInitiator, paymentReq) paymentSender.expectMsgType[UUID](30 seconds) @@ -624,7 +625,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val preimage: ByteVector = randomBytes32 val paymentHash = Crypto.sha256(preimage) // A sends a payment to F - val paymentReq = SendPayment(100000000L, paymentHash, nodes("F3").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams) + val paymentReq = SendPayment(MilliSatoshi(100000000L), paymentHash, nodes("F3").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams) val paymentSender = TestProbe() paymentSender.send(nodes("A").paymentInitiator, paymentReq) val paymentId = paymentSender.expectMsgType[UUID] @@ -686,7 +687,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val preimage: ByteVector = randomBytes32 val paymentHash = Crypto.sha256(preimage) // A sends a payment to F - val paymentReq = SendPayment(100000000L, paymentHash, nodes("F4").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams) + val paymentReq = SendPayment(MilliSatoshi(100000000L), paymentHash, nodes("F4").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams) val paymentSender = TestProbe() paymentSender.send(nodes("A").paymentInitiator, paymentReq) val paymentId = paymentSender.expectMsgType[UUID](30 seconds) @@ -760,7 +761,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val amountMsat = MilliSatoshi(300000000L) sender.send(paymentHandlerF, ReceivePayment(Some(amountMsat), "1 coffee")) val pr = sender.expectMsgType[PaymentRequest] - val sendReq = SendPayment(300000000L, pr.paymentHash, pr.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 1) + val sendReq = SendPayment(MilliSatoshi(300000000L), pr.paymentHash, pr.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 1) sender.send(nodes("A").paymentInitiator, sendReq) val paymentId = sender.expectMsgType[UUID] // we forward the htlc to the payment handler @@ -771,8 +772,8 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService sender.expectMsgType[PaymentSucceeded].id === paymentId // we now send a few htlcs C->F and F->C in order to obtain a commitments with multiple htlcs - def send(amountMsat: Long, paymentHandler: ActorRef, paymentInitiator: ActorRef) = { - sender.send(paymentHandler, ReceivePayment(Some(MilliSatoshi(amountMsat)), "1 coffee")) + def send(amountMsat: MilliSatoshi, paymentHandler: ActorRef, paymentInitiator: ActorRef) = { + sender.send(paymentHandler, ReceivePayment(Some(amountMsat), "1 coffee")) val pr = sender.expectMsgType[PaymentRequest] val sendReq = SendPayment(amountMsat, pr.paymentHash, pr.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 1) sender.send(paymentInitiator, sendReq) @@ -780,19 +781,19 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService } val buffer = TestProbe() - send(100000000, paymentHandlerF, nodes("C").paymentInitiator) // will be left pending + send(MilliSatoshi(100000000), paymentHandlerF, nodes("C").paymentInitiator) // will be left pending forwardHandlerF.expectMsgType[UpdateAddHtlc] forwardHandlerF.forward(buffer.ref) sigListener.expectMsgType[ChannelSignatureReceived] - send(110000000, paymentHandlerF, nodes("C").paymentInitiator) // will be left pending + send(MilliSatoshi(110000000), paymentHandlerF, nodes("C").paymentInitiator) // will be left pending forwardHandlerF.expectMsgType[UpdateAddHtlc] forwardHandlerF.forward(buffer.ref) sigListener.expectMsgType[ChannelSignatureReceived] - send(120000000, paymentHandlerC, nodes("F5").paymentInitiator) + send(MilliSatoshi(120000000), paymentHandlerC, nodes("F5").paymentInitiator) forwardHandlerC.expectMsgType[UpdateAddHtlc] forwardHandlerC.forward(buffer.ref) sigListener.expectMsgType[ChannelSignatureReceived] - send(130000000, paymentHandlerC, nodes("F5").paymentInitiator) + send(MilliSatoshi(130000000), paymentHandlerC, nodes("F5").paymentInitiator) forwardHandlerC.expectMsgType[UpdateAddHtlc] forwardHandlerC.forward(buffer.ref) val commitmentsF = sigListener.expectMsgType[ChannelSignatureReceived].commitments diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala index 6697116750..9a0fbfde08 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala @@ -21,14 +21,14 @@ import java.util.concurrent.{CountDownLatch, TimeUnit} import akka.actor.{ActorRef, ActorSystem, Props} import akka.testkit.{TestFSMRef, TestKit, TestProbe} -import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.bitcoin.{ByteVector32, Satoshi} 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, TestUtils} +import fr.acinq.eclair.{Globals, MilliSatoshi, TestUtils} import org.scalatest.{BeforeAndAfterAll, Matchers, Outcome, fixture} import scala.concurrent.duration._ @@ -60,7 +60,7 @@ class RustyTestsSpec extends TestKit(ActorSystem("test")) with Matchers with fix val bobInit = Init(Bob.channelParams.globalFeatures, Bob.channelParams.localFeatures) // alice and bob will both have 1 000 000 sat feeEstimator.setFeerate(FeeratesPerKw.single(10000)) - alice ! INPUT_INIT_FUNDER(ByteVector32.Zeroes, 2000000, 1000000000, feeEstimator.getFeeratePerKw(target = 2), feeEstimator.getFeeratePerKw(target = 6), Alice.channelParams, pipe, bobInit, ChannelFlags.Empty) + alice ! INPUT_INIT_FUNDER(ByteVector32.Zeroes, Satoshi(2000000), MilliSatoshi(1000000000), feeEstimator.getFeeratePerKw(target = 2), feeEstimator.getFeeratePerKw(target = 6), Alice.channelParams, pipe, bobInit, ChannelFlags.Empty) bob ! INPUT_INIT_FUNDEE(ByteVector32.Zeroes, Bob.channelParams, pipe, aliceInit) pipe ! (alice, bob) within(30 seconds) { diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/SynchronizationPipe.scala b/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/SynchronizationPipe.scala index 2c72623576..1a2f5eabe6 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/SynchronizationPipe.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/SynchronizationPipe.scala @@ -22,7 +22,7 @@ import java.util.concurrent.CountDownLatch import akka.actor.{Actor, ActorLogging, ActorRef, Stash} import fr.acinq.bitcoin.ByteVector32 -import fr.acinq.eclair.{TestConstants, TestUtils} +import fr.acinq.eclair.{MilliSatoshi, TestConstants, TestUtils} import fr.acinq.eclair.channel._ import fr.acinq.eclair.transactions.{IN, OUT} @@ -57,7 +57,7 @@ class SynchronizationPipe(latch: CountDownLatch) extends Actor with ActorLogging script match { case offer(x, amount, rhash) :: rest => - resolve(x) ! CMD_ADD_HTLC(amount.toInt, ByteVector32.fromValidHex(rhash), 144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + resolve(x) ! CMD_ADD_HTLC(MilliSatoshi(amount.toInt), ByteVector32.fromValidHex(rhash), 144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) exec(rest, a, b) case fulfill(x, id, r) :: rest => resolve(x) ! CMD_FULFILL_HTLC(id.toInt, ByteVector32.fromValidHex(r)) @@ -134,15 +134,15 @@ class SynchronizationPipe(latch: CountDownLatch) extends Actor with ActorLogging s" Commit ${d.commitments.localCommit.index}:", s" Offered htlcs: ${localCommit.spec.htlcs.filter(_.direction == OUT).map(h => (h.add.id, h.add.amountMsat)).mkString(" ")}", s" Received htlcs: ${localCommit.spec.htlcs.filter(_.direction == IN).map(h => (h.add.id, h.add.amountMsat)).mkString(" ")}", - s" Balance us: ${localCommit.spec.toLocalMsat}", - s" Balance them: ${localCommit.spec.toRemoteMsat}", + s" Balance us: ${localCommit.spec.toLocal}", + s" Balance them: ${localCommit.spec.toRemote}", s" Fee rate: ${localCommit.spec.feeratePerKw}", "REMOTE COMMITS:", s" Commit ${remoteCommit.index}:", s" Offered htlcs: ${remoteCommit.spec.htlcs.filter(_.direction == OUT).map(h => (h.add.id, h.add.amountMsat)).mkString(" ")}", s" Received htlcs: ${remoteCommit.spec.htlcs.filter(_.direction == IN).map(h => (h.add.id, h.add.amountMsat)).mkString(" ")}", - s" Balance us: ${remoteCommit.spec.toLocalMsat}", - s" Balance them: ${remoteCommit.spec.toRemoteMsat}", + s" Balance us: ${remoteCommit.spec.toLocal}", + s" Balance them: ${remoteCommit.spec.toRemote}", s" Fee rate: ${remoteCommit.spec.feeratePerKw}") .foreach(s => { fout.write(rtrim(s)) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/io/HtlcReaperSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/io/HtlcReaperSpec.scala index 4378f5cf1d..57debf6a86 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/io/HtlcReaperSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/io/HtlcReaperSpec.scala @@ -18,6 +18,7 @@ package fr.acinq.eclair.io import akka.actor.{ActorSystem, Props} import akka.testkit.{TestKit, TestProbe} +import fr.acinq.eclair.MilliSatoshi import fr.acinq.eclair.channel._ import fr.acinq.eclair.{TestConstants, randomBytes32} import fr.acinq.eclair.wire.{ChannelCodecsSpec, TemporaryNodeFailure, UpdateAddHtlc} @@ -36,11 +37,11 @@ class HtlcReaperSpec extends TestKit(ActorSystem("test")) with FunSuiteLike { val data = ChannelCodecsSpec.normal // assuming that data has incoming htlcs 0 and 1, we don't care about the amount/payment_hash/onion fields - val add0 = UpdateAddHtlc(data.channelId, 0, 20000, randomBytes32, 100, TestConstants.emptyOnionPacket) - val add1 = UpdateAddHtlc(data.channelId, 1, 30000, randomBytes32, 100, TestConstants.emptyOnionPacket) + val add0 = UpdateAddHtlc(data.channelId, 0, MilliSatoshi(20000), randomBytes32, 100, TestConstants.emptyOnionPacket) + val add1 = UpdateAddHtlc(data.channelId, 1, MilliSatoshi(30000), randomBytes32, 100, TestConstants.emptyOnionPacket) // unrelated htlc - val add99 = UpdateAddHtlc(randomBytes32, 0, 12345678, randomBytes32, 100, TestConstants.emptyOnionPacket) + val add99 = UpdateAddHtlc(randomBytes32, 0, MilliSatoshi(12345678), randomBytes32, 100, TestConstants.emptyOnionPacket) val brokenHtlcs = Seq(add0, add1, add99) val brokenHtlcKiller = system.actorOf(Props[HtlcReaper], name = "htlc-reaper") diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala index 76fd945de6..d53d86ddba 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala @@ -22,7 +22,7 @@ import akka.actor.FSM.{CurrentState, SubscribeTransitionCallBack, Transition} import akka.actor.{ActorRef, PoisonPill} import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{MilliSatoshi, Satoshi} +import fr.acinq.bitcoin.{Satoshi} import fr.acinq.eclair.TestConstants._ import fr.acinq.eclair._ import fr.acinq.eclair.blockchain.{EclairWallet, TestWallet} diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/ChannelSelectionSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/ChannelSelectionSpec.scala index ccc65efe0b..71027c5e10 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/ChannelSelectionSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/ChannelSelectionSpec.scala @@ -22,7 +22,7 @@ import fr.acinq.eclair.channel.{CMD_ADD_HTLC, CMD_FAIL_HTLC} import fr.acinq.eclair.payment.Relayer.{OutgoingChannel, RelayFailure, RelayPayload, RelaySuccess} import fr.acinq.eclair.router.Announcements import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{ShortChannelId, TestConstants, randomBytes32, randomKey} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, TestConstants, randomBytes32, randomKey} import fr.acinq.eclair.payment.HtlcGenerationSpec.makeCommitments import org.scalatest.FunSuite @@ -33,17 +33,17 @@ class ChannelSelectionSpec extends FunSuite { /** * This is just a simplified helper function with random values for fields we are not using here */ - def dummyUpdate(shortChannelId: ShortChannelId, cltvExpiryDelta: Int, htlcMinimumMsat: Long, feeBaseMsat: Long, feeProportionalMillionths: Long, htlcMaximumMsat: Long, enable: Boolean = true) = - Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, shortChannelId, cltvExpiryDelta, htlcMinimumMsat, feeBaseMsat, feeProportionalMillionths, htlcMaximumMsat, enable) + def dummyUpdate(shortChannelId: ShortChannelId, cltvExpiryDelta: Int, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: Long, feeProportionalMillionths: Long, htlcMaximumMsat: Long, enable: Boolean = true) = + Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, shortChannelId, cltvExpiryDelta, htlcMinimumMsat, MilliSatoshi(feeBaseMsat), feeProportionalMillionths, MilliSatoshi(htlcMaximumMsat), enable) test("convert to CMD_FAIL_HTLC/CMD_ADD_HTLC") { val relayPayload = RelayPayload( - add = UpdateAddHtlc(randomBytes32, 42, 1000000, randomBytes32, 70, TestConstants.emptyOnionPacket), - payload = PerHopPayload(ShortChannelId(12345), amtToForward = 998900, outgoingCltvValue = 60), + add = UpdateAddHtlc(randomBytes32, 42, MilliSatoshi(1000000), randomBytes32, 70, TestConstants.emptyOnionPacket), + payload = PerHopPayload(ShortChannelId(12345), amtToForward = MilliSatoshi(998900), outgoingCltvValue = 60), nextPacket = TestConstants.emptyOnionPacket // just a placeholder ) - val channelUpdate = dummyUpdate(ShortChannelId(12345), 10, 100, 1000, 100, 10000000, true) + val channelUpdate = dummyUpdate(ShortChannelId(12345), 10, MilliSatoshi(100), 1000, 100, 10000000, true) implicit val log = akka.event.NoLogging @@ -55,36 +55,36 @@ class ChannelSelectionSpec extends FunSuite { val channelUpdate_disabled = channelUpdate.copy(channelFlags = Announcements.makeChannelFlags(true, enable = false)) assert(Relayer.relayOrFail(relayPayload, Some(channelUpdate_disabled)) === RelayFailure(CMD_FAIL_HTLC(relayPayload.add.id, Right(ChannelDisabled(channelUpdate_disabled.messageFlags, channelUpdate_disabled.channelFlags, channelUpdate_disabled)), commit = true))) // amount too low - val relayPayload_toolow = relayPayload.copy(payload = relayPayload.payload.copy(amtToForward = 99)) + val relayPayload_toolow = relayPayload.copy(payload = relayPayload.payload.copy(amtToForward = MilliSatoshi(99))) assert(Relayer.relayOrFail(relayPayload_toolow, Some(channelUpdate)) === RelayFailure(CMD_FAIL_HTLC(relayPayload.add.id, Right(AmountBelowMinimum(relayPayload_toolow.payload.amtToForward, channelUpdate)), commit = true))) // incorrect cltv expiry val relayPayload_incorrectcltv = relayPayload.copy(payload = relayPayload.payload.copy(outgoingCltvValue = 42)) assert(Relayer.relayOrFail(relayPayload_incorrectcltv, Some(channelUpdate)) === RelayFailure(CMD_FAIL_HTLC(relayPayload.add.id, Right(IncorrectCltvExpiry(relayPayload_incorrectcltv.payload.outgoingCltvValue, channelUpdate)), commit = true))) // insufficient fee - val relayPayload_insufficientfee = relayPayload.copy(payload = relayPayload.payload.copy(amtToForward = 998910)) + val relayPayload_insufficientfee = relayPayload.copy(payload = relayPayload.payload.copy(amtToForward = MilliSatoshi(998910))) assert(Relayer.relayOrFail(relayPayload_insufficientfee, Some(channelUpdate)) === RelayFailure(CMD_FAIL_HTLC(relayPayload.add.id, Right(FeeInsufficient(relayPayload_insufficientfee.add.amountMsat, channelUpdate)), commit = true))) // note that a generous fee is ok! - val relayPayload_highfee = relayPayload.copy(payload = relayPayload.payload.copy(amtToForward = 900000)) + val relayPayload_highfee = relayPayload.copy(payload = relayPayload.payload.copy(amtToForward = MilliSatoshi(900000))) assert(Relayer.relayOrFail(relayPayload_highfee, Some(channelUpdate)) === RelaySuccess(ShortChannelId(12345), CMD_ADD_HTLC(relayPayload_highfee.payload.amtToForward, relayPayload_highfee.add.paymentHash, relayPayload_highfee.payload.outgoingCltvValue, relayPayload_highfee.nextPacket, upstream = Right(relayPayload.add), commit = true))) } test("channel selection") { val relayPayload = RelayPayload( - add = UpdateAddHtlc(randomBytes32, 42, 1000000, randomBytes32, 70, TestConstants.emptyOnionPacket), - payload = PerHopPayload(ShortChannelId(12345), amtToForward = 998900, outgoingCltvValue = 60), + add = UpdateAddHtlc(randomBytes32, 42, MilliSatoshi(1000000), randomBytes32, 70, TestConstants.emptyOnionPacket), + payload = PerHopPayload(ShortChannelId(12345), amtToForward = MilliSatoshi(998900), outgoingCltvValue = 60), nextPacket = TestConstants.emptyOnionPacket // just a placeholder ) val (a, b) = (randomKey.publicKey, randomKey.publicKey) - val channelUpdate = dummyUpdate(ShortChannelId(12345), 10, 100, 1000, 100, 10000000, true) + val channelUpdate = dummyUpdate(ShortChannelId(12345), 10, MilliSatoshi(100), 1000, 100, 10000000, true) val channelUpdates = Map( - ShortChannelId(11111) -> OutgoingChannel(a, channelUpdate, makeCommitments(ByteVector32.Zeroes, 100000000)), - ShortChannelId(12345) -> OutgoingChannel(a, channelUpdate, makeCommitments(ByteVector32.Zeroes, 20000000)), - ShortChannelId(22222) -> OutgoingChannel(a, channelUpdate, makeCommitments(ByteVector32.Zeroes, 10000000)), - ShortChannelId(33333) -> OutgoingChannel(a, channelUpdate, makeCommitments(ByteVector32.Zeroes, 100000)), - ShortChannelId(44444) -> OutgoingChannel(b, channelUpdate, makeCommitments(ByteVector32.Zeroes, 1000000)) + ShortChannelId(11111) -> OutgoingChannel(a, channelUpdate, makeCommitments(ByteVector32.Zeroes, MilliSatoshi(100000000))), + ShortChannelId(12345) -> OutgoingChannel(a, channelUpdate, makeCommitments(ByteVector32.Zeroes, MilliSatoshi(20000000))), + ShortChannelId(22222) -> OutgoingChannel(a, channelUpdate, makeCommitments(ByteVector32.Zeroes, MilliSatoshi(10000000))), + ShortChannelId(33333) -> OutgoingChannel(a, channelUpdate, makeCommitments(ByteVector32.Zeroes, MilliSatoshi(100000))), + ShortChannelId(44444) -> OutgoingChannel(b, channelUpdate, makeCommitments(ByteVector32.Zeroes, MilliSatoshi(1000000))) ) val node2channels = new mutable.HashMap[PublicKey, mutable.Set[ShortChannelId]] with mutable.MultiMap[PublicKey, ShortChannelId] @@ -104,11 +104,11 @@ class ChannelSelectionSpec extends FunSuite { // all the suitable channels have been tried assert(Relayer.selectPreferredChannel(relayPayload, channelUpdates, node2channels, Seq(ShortChannelId(22222), ShortChannelId(12345), ShortChannelId(11111))) === None) // higher amount payment (have to increased incoming htlc amount for fees to be sufficient) - assert(Relayer.selectPreferredChannel(relayPayload.modify(_.add.amountMsat).setTo(60000000).modify(_.payload.amtToForward).setTo(50000000), channelUpdates, node2channels, Seq.empty) === Some(ShortChannelId(11111))) + assert(Relayer.selectPreferredChannel(relayPayload.modify(_.add.amountMsat).setTo(MilliSatoshi(60000000)).modify(_.payload.amtToForward).setTo(MilliSatoshi(50000000)), channelUpdates, node2channels, Seq.empty) === Some(ShortChannelId(11111))) // lower amount payment - assert(Relayer.selectPreferredChannel(relayPayload.modify(_.payload.amtToForward).setTo(1000), channelUpdates, node2channels, Seq.empty) === Some(ShortChannelId(33333))) + assert(Relayer.selectPreferredChannel(relayPayload.modify(_.payload.amtToForward).setTo(MilliSatoshi(1000)), channelUpdates, node2channels, Seq.empty) === Some(ShortChannelId(33333))) // payment too high, no suitable channel found - assert(Relayer.selectPreferredChannel(relayPayload.modify(_.payload.amtToForward).setTo(1000000000), channelUpdates, node2channels, Seq.empty) === Some(ShortChannelId(12345))) + assert(Relayer.selectPreferredChannel(relayPayload.modify(_.payload.amtToForward).setTo(MilliSatoshi(1000000000)), channelUpdates, node2channels, Seq.empty) === Some(ShortChannelId(12345))) // invalid cltv expiry, no suitable channel, we keep the requested one assert(Relayer.selectPreferredChannel(relayPayload.modify(_.payload.outgoingCltvValue).setTo(40), channelUpdates, node2channels, Seq.empty) === Some(ShortChannelId(12345))) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/HtlcGenerationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/HtlcGenerationSpec.scala index 9114a8e418..02252bc923 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/HtlcGenerationSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/HtlcGenerationSpec.scala @@ -20,13 +20,14 @@ import java.util.UUID import fr.acinq.bitcoin.DeterministicWallet.ExtendedPrivateKey import fr.acinq.bitcoin.{Block, ByteVector32, Crypto, DeterministicWallet} +import fr.acinq.eclair.maxOf import fr.acinq.eclair.channel.{Channel, ChannelVersion, Commitments} import fr.acinq.eclair.crypto.Sphinx import fr.acinq.eclair.crypto.Sphinx.{DecryptedPacket, PacketAndSecrets} import fr.acinq.eclair.payment.PaymentLifecycle._ import fr.acinq.eclair.router.Hop import fr.acinq.eclair.wire.{ChannelUpdate, OnionCodecs, PerHopPayload} -import fr.acinq.eclair.{ShortChannelId, TestConstants, nodeFee, randomBytes32} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, TestConstants, nodeFee, randomBytes32} import org.scalatest.FunSuite import scodec.bits.ByteVector @@ -37,9 +38,9 @@ import scodec.bits.ByteVector class HtlcGenerationSpec extends FunSuite { test("compute fees") { - val feeBaseMsat = 150000L + val feeBaseMsat = MilliSatoshi(150000L) val feeProportionalMillionth = 4L - val htlcAmountMsat = 42000000 + val htlcAmountMsat = MilliSatoshi(42000000) // spec: fee-base-msat + htlc-amount-msat * fee-proportional-millionths / 1000000 val ref = feeBaseMsat + htlcAmountMsat * feeProportionalMillionth / 1000000 val fee = nodeFee(feeBaseMsat, feeProportionalMillionth, htlcAmountMsat) @@ -98,7 +99,7 @@ class HtlcGenerationSpec extends FunSuite { val (add, _) = buildCommand(UUID.randomUUID, finalAmountMsat, finalExpiry, paymentHash, hops) - assert(add.amountMsat > finalAmountMsat) + assert(add.amount > finalAmountMsat) assert(add.cltvExpiry === finalExpiry + channelUpdate_de.cltvExpiryDelta + channelUpdate_cd.cltvExpiryDelta + channelUpdate_bc.cltvExpiryDelta) assert(add.paymentHash === paymentHash) assert(add.onion.payload.length === Sphinx.PaymentPacket.PayloadLength) @@ -132,7 +133,7 @@ class HtlcGenerationSpec extends FunSuite { test("build a command with no hops") { val (add, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops.take(1)) - assert(add.amountMsat === finalAmountMsat) + assert(add.amount === finalAmountMsat) assert(add.cltvExpiry === finalExpiry) assert(add.paymentHash === paymentHash) assert(add.onion.payload.length === Sphinx.PaymentPacket.PayloadLength) @@ -149,10 +150,10 @@ class HtlcGenerationSpec extends FunSuite { object HtlcGenerationSpec { - def makeCommitments(channelId: ByteVector32, availableBalanceForSend: Long = 50000000L, availableBalanceForReceive: Long = 50000000L) = + def makeCommitments(channelId: ByteVector32, testAvailableBalanceForSend: MilliSatoshi = MilliSatoshi(50000000L), testAvailableBalanceForReceive: MilliSatoshi = MilliSatoshi(50000000L)) = new Commitments(ChannelVersion.STANDARD, null, null, 0.toByte, null, null, null, null, 0, 0, Map.empty, null, null, null, channelId) { - override lazy val availableBalanceForSendMsat: Long = availableBalanceForSend.max(0) - override lazy val availableBalanceForReceiveMsat: Long = availableBalanceForReceive.max(0) + override lazy val availableBalanceForSend: MilliSatoshi = maxOf(testAvailableBalanceForSend, MilliSatoshi(0)) + override lazy val availableBalanceForReceive: MilliSatoshi = maxOf(testAvailableBalanceForReceive, MilliSatoshi(0)) } def randomExtendedPrivateKey: ExtendedPrivateKey = DeterministicWallet.generate(randomBytes32) @@ -160,11 +161,11 @@ object HtlcGenerationSpec { val (priv_a, priv_b, priv_c, priv_d, priv_e) = (TestConstants.Alice.keyManager.nodeKey, TestConstants.Bob.keyManager.nodeKey, randomExtendedPrivateKey, randomExtendedPrivateKey, randomExtendedPrivateKey) val (a, b, c, d, e) = (priv_a.publicKey, priv_b.publicKey, priv_c.publicKey, priv_d.publicKey, priv_e.publicKey) val sig = Crypto.sign(Crypto.sha256(ByteVector.empty), priv_a.privateKey) - val defaultChannelUpdate = ChannelUpdate(sig, Block.RegtestGenesisBlock.hash, ShortChannelId(0), 0, 1, 0, 0, 42000, 0, 0, Some(500000000L)) - val channelUpdate_ab = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(1), cltvExpiryDelta = 4, feeBaseMsat = 642000, feeProportionalMillionths = 7) - val channelUpdate_bc = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(2), cltvExpiryDelta = 5, feeBaseMsat = 153000, feeProportionalMillionths = 4) - val channelUpdate_cd = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(3), cltvExpiryDelta = 10, feeBaseMsat = 60000, feeProportionalMillionths = 1) - val channelUpdate_de = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(4), cltvExpiryDelta = 7, feeBaseMsat = 766000, feeProportionalMillionths = 10) + val defaultChannelUpdate = ChannelUpdate(sig, Block.RegtestGenesisBlock.hash, ShortChannelId(0), 0, 1, 0, 0, MilliSatoshi(42000), MilliSatoshi(0), 0, Some(MilliSatoshi(500000000L))) + val channelUpdate_ab = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(1), cltvExpiryDelta = 4, feeBaseMsat = MilliSatoshi(642000), feeProportionalMillionths = 7) + val channelUpdate_bc = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(2), cltvExpiryDelta = 5, feeBaseMsat = MilliSatoshi(153000), feeProportionalMillionths = 4) + val channelUpdate_cd = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(3), cltvExpiryDelta = 10, feeBaseMsat = MilliSatoshi(60000), feeProportionalMillionths = 1) + val channelUpdate_de = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(4), cltvExpiryDelta = 7, feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10) // simple route a -> b -> c -> d -> e @@ -174,7 +175,7 @@ object HtlcGenerationSpec { Hop(c, d, channelUpdate_cd) :: Hop(d, e, channelUpdate_de) :: Nil - val finalAmountMsat = 42000000L + val finalAmountMsat = MilliSatoshi(42000000L) val currentBlockCount = 420000 val finalExpiry = currentBlockCount + Channel.MIN_CLTV_EXPIRY val paymentPreimage = randomBytes32 diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentHandlerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentHandlerSpec.scala index 3915d24895..4a1b21f452 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentHandlerSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentHandlerSpec.scala @@ -19,13 +19,13 @@ package fr.acinq.eclair.payment import akka.actor.Status.Failure import akka.actor.ActorSystem import akka.testkit.{TestActorRef, TestKit, TestProbe} -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi} +import fr.acinq.bitcoin.ByteVector32 import fr.acinq.eclair.TestConstants.Alice 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.{FinalExpiryTooSoon, UpdateAddHtlc} -import fr.acinq.eclair.{Globals, ShortChannelId, TestConstants, randomKey} +import fr.acinq.eclair.{Globals, MilliSatoshi, ShortChannelId, TestConstants, randomKey} import org.scalatest.FunSuiteLike import scodec.bits.ByteVector @@ -54,7 +54,7 @@ class PaymentHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLike assert(nodeParams.db.payments.getPendingPaymentRequestAndPreimage(pr.paymentHash).isDefined) assert(!nodeParams.db.payments.getPendingPaymentRequestAndPreimage(pr.paymentHash).get._2.isExpired) - val add = UpdateAddHtlc(ByteVector32(ByteVector.fill(32)(1)), 0, amountMsat.amount, pr.paymentHash, expiry, TestConstants.emptyOnionPacket) + val add = UpdateAddHtlc(ByteVector32(ByteVector.fill(32)(1)), 0, amountMsat, pr.paymentHash, expiry, TestConstants.emptyOnionPacket) sender.send(handler, add) sender.expectMsgType[CMD_FULFILL_HTLC] @@ -68,7 +68,7 @@ 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.amount, pr.paymentHash, expiry, TestConstants.emptyOnionPacket) + val add = UpdateAddHtlc(ByteVector32(ByteVector.fill(32)(1)), 0, amountMsat, pr.paymentHash, expiry, TestConstants.emptyOnionPacket) sender.send(handler, add) sender.expectMsgType[CMD_FULFILL_HTLC] val paymentRelayed = eventListener.expectMsgType[PaymentReceived] @@ -81,7 +81,7 @@ 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.amount, pr.paymentHash, cltvExpiry = Globals.blockCount.get() + 3, TestConstants.emptyOnionPacket) + val add = UpdateAddHtlc(ByteVector32(ByteVector.fill(32)(1)), 0, amountMsat, pr.paymentHash, cltvExpiry = Globals.blockCount.get() + 3, TestConstants.emptyOnionPacket) sender.send(handler, add) assert(sender.expectMsgType[CMD_FAIL_HTLC].reason == Right(FinalExpiryTooSoon)) eventListener.expectNoMsg(300 milliseconds) @@ -164,7 +164,7 @@ class PaymentHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLike sender.send(handler, ReceivePayment(Some(amountMsat), "some desc", expirySeconds_opt = Some(0))) val pr = sender.expectMsgType[PaymentRequest] - val add = UpdateAddHtlc(ByteVector32(ByteVector.fill(32)(1)), 0, amountMsat.amount, pr.paymentHash, expiry, TestConstants.emptyOnionPacket) + val add = UpdateAddHtlc(ByteVector32(ByteVector.fill(32)(1)), 0, amountMsat, pr.paymentHash, expiry, TestConstants.emptyOnionPacket) sender.send(handler, add) sender.expectMsgType[CMD_FAIL_HTLC] diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala index 79192309bf..9d178c0819 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala @@ -22,7 +22,7 @@ import akka.actor.FSM.{CurrentState, SubscribeTransitionCallBack, Transition} import akka.actor.Status import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.Script.{pay2wsh, write} -import fr.acinq.bitcoin.{Block, ByteVector32, MilliSatoshi, Satoshi, Transaction, TxOut} +import fr.acinq.bitcoin.{Block, ByteVector32, Satoshi, Transaction, TxOut} import fr.acinq.eclair.blockchain.{UtxoStatus, ValidateRequest, ValidateResult, WatchSpentBasic} import fr.acinq.eclair.channel.Register.ForwardShortId import fr.acinq.eclair.channel.{AddHtlcFailed, ChannelUnavailable} @@ -42,7 +42,7 @@ import fr.acinq.eclair._ class PaymentLifecycleSpec extends BaseRouterSpec { - val defaultAmountMsat = 142000000L + val defaultAmountMsat = MilliSatoshi(142000000L) test("send to route") { fixture => import fixture._ @@ -109,7 +109,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { paymentFSM ! SubscribeTransitionCallBack(monitor.ref) val CurrentState(_, WAITING_FOR_REQUEST) = monitor.expectMsgClass(classOf[CurrentState[_]]) - val request = SendPayment(defaultAmountMsat, randomBytes32, d, routeParams = Some(RouteParams(randomize = false, maxFeeBaseMsat = 100, maxFeePct = 0.0, routeMaxLength = 20, routeMaxCltv = 2016, ratios = None)), maxAttempts = 5) + val request = SendPayment(defaultAmountMsat, randomBytes32, d, routeParams = Some(RouteParams(randomize = false, maxFeeBase = MilliSatoshi(100), maxFeePct = 0.0, routeMaxLength = 20, routeMaxCltv = 2016, ratios = None)), maxAttempts = 5) sender.send(paymentFSM, request) val Transition(_, WAITING_FOR_REQUEST, WAITING_FOR_ROUTE) = monitor.expectMsgClass(classOf[Transition[_]]) @@ -389,9 +389,9 @@ class PaymentLifecycleSpec extends BaseRouterSpec { sender.send(paymentFSM, UpdateFulfillHtlc(ByteVector32.Zeroes, 0, defaultPaymentHash)) val paymentOK = sender.expectMsgType[PaymentSucceeded] - val PaymentSent(_, MilliSatoshi(request.amountMsat), fee, request.paymentHash, paymentOK.paymentPreimage, _, _) = eventListener.expectMsgType[PaymentSent] + val PaymentSent(_, request.amount, fee, request.paymentHash, paymentOK.paymentPreimage, _, _) = eventListener.expectMsgType[PaymentSent] assert(fee > MilliSatoshi(0)) - assert(fee === MilliSatoshi(paymentOK.amountMsat - request.amountMsat)) + assert(fee === paymentOK.amount - request.amount) awaitCond(paymentDb.getOutgoingPayment(id).exists(_.status == OutgoingPaymentStatus.SUCCEEDED)) } @@ -409,8 +409,8 @@ class PaymentLifecycleSpec extends BaseRouterSpec { val ann_g = makeNodeAnnouncement(priv_g, "node-G", Color(-30, 10, -50), Nil) val channelId_bg = ShortChannelId(420000, 5, 0) val chan_bg = channelAnnouncement(channelId_bg, priv_b, priv_g, priv_funding_b, priv_funding_g) - val channelUpdate_bg = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, g, channelId_bg, cltvExpiryDelta = 9, htlcMinimumMsat = 0, feeBaseMsat = 0, feeProportionalMillionths = 0, htlcMaximumMsat = 500000000L) - val channelUpdate_gb = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_g, b, channelId_bg, cltvExpiryDelta = 9, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 8, htlcMaximumMsat = 500000000L) + val channelUpdate_bg = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, g, channelId_bg, cltvExpiryDelta = 9, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(0), feeProportionalMillionths = 0, htlcMaximumMsat = MilliSatoshi(500000000L)) + val channelUpdate_gb = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_g, b, channelId_bg, cltvExpiryDelta = 9, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 8, htlcMaximumMsat = MilliSatoshi(500000000L)) assert(Router.getDesc(channelUpdate_bg, chan_bg) === ChannelDesc(chan_bg.shortChannelId, priv_b.publicKey, priv_g.publicKey)) router ! PeerRoutingMessage(null, remoteNodeId, chan_bg) router ! PeerRoutingMessage(null, remoteNodeId, ann_g) @@ -441,13 +441,13 @@ class PaymentLifecycleSpec extends BaseRouterSpec { sender.send(paymentFSM, UpdateFulfillHtlc(ByteVector32.Zeroes, 0, defaultPaymentHash)) val paymentOK = sender.expectMsgType[PaymentSucceeded] - val PaymentSent(_, MilliSatoshi(request.amountMsat), fee, request.paymentHash, paymentOK.paymentPreimage, _, _) = eventListener.expectMsgType[PaymentSent] + val PaymentSent(_, request.amount, fee, request.paymentHash, paymentOK.paymentPreimage, _, _) = eventListener.expectMsgType[PaymentSent] // during the route computation the fees were treated as if they were 1msat but when sending the onion we actually put zero // NB: A -> B doesn't pay fees because it's our direct neighbor // NB: B -> G doesn't asks for fees at all assert(fee === MilliSatoshi(0)) - assert(fee === MilliSatoshi(paymentOK.amountMsat - request.amountMsat)) + assert(fee === paymentOK.amount - request.amount) } test("filter errors properly") { _ => diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentRequestSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentRequestSpec.scala index f67bd0e074..19706bcac9 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentRequestSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentRequestSpec.scala @@ -17,14 +17,15 @@ package fr.acinq.eclair.payment import java.nio.ByteOrder - import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin.{Block, Btc, ByteVector32, Crypto, MilliBtc, MilliSatoshi, Protocol, Satoshi} -import fr.acinq.eclair.ShortChannelId +import fr.acinq.bitcoin.{Block, Btc, ByteVector32, Crypto, MilliBtc, Protocol, Satoshi} +import fr.acinq.bitcoin._ +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId} import fr.acinq.eclair.payment.PaymentRequest._ import org.scalatest.FunSuite import scodec.DecodeResult import scodec.bits._ +import fr.acinq.eclair._ /** * Created by fabrice on 15/05/17. @@ -42,13 +43,13 @@ class PaymentRequestSpec extends FunSuite { assert('p' === Amount.unit(MilliSatoshi(99))) assert('n' === Amount.unit(MilliSatoshi(100))) assert('p' === Amount.unit(MilliSatoshi(101))) - assert('n' === Amount.unit(Satoshi(1))) - assert('u' === Amount.unit(Satoshi(100))) - assert('n' === Amount.unit(Satoshi(101))) - assert('u' === Amount.unit(Satoshi(1155400))) - assert('m' === Amount.unit(MilliBtc(1))) - assert('m' === Amount.unit(MilliBtc(10))) - assert('m' === Amount.unit(Btc(1))) + assert('n' === Amount.unit(Satoshi(1).toMilliSatoshi)) + assert('u' === Amount.unit(Satoshi(100).toMilliSatoshi)) + assert('n' === Amount.unit(Satoshi(101).toMilliSatoshi)) + assert('u' === Amount.unit(Satoshi(1155400).toMilliSatoshi)) + assert('m' === Amount.unit(millibtc2satoshi(MilliBtc(1)).toMilliSatoshi)) + assert('m' === Amount.unit(millibtc2satoshi(MilliBtc(10)).toMilliSatoshi)) + assert('m' === Amount.unit(btc2satoshi(Btc(1)).toMilliSatoshi)) } test("check that we can still decode non-minimal amount encoding") { diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/RelayerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/RelayerSpec.scala index c6f6a27d41..97db737ee4 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/RelayerSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/RelayerSpec.scala @@ -20,13 +20,13 @@ import java.util.UUID import akka.actor.{ActorRef, Status} import akka.testkit.TestProbe -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi} +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.eclair.channel._ import fr.acinq.eclair.crypto.Sphinx import fr.acinq.eclair.payment.PaymentLifecycle.buildCommand import fr.acinq.eclair.router.Announcements import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{ShortChannelId, TestConstants, TestkitBaseClass, UInt64, randomBytes32} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, TestConstants, TestkitBaseClass, UInt64, randomBytes32} import org.scalatest.Outcome import scodec.bits.ByteVector @@ -63,7 +63,7 @@ class RelayerSpec extends TestkitBaseClass { // we use this to build a valid onion val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops) // and then manually build an htlc - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) @@ -83,14 +83,14 @@ class RelayerSpec extends TestkitBaseClass { // we use this to build a valid onion val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops) // and then manually build an htlc - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) // we tell the relayer about channel B-C relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) // this is another channel B-C, with less balance (it will be preferred) val (channelId_bc_1, channelUpdate_bc_1) = (randomBytes32, channelUpdate_bc.copy(shortChannelId = ShortChannelId("500000x1x1"))) - relayer ! LocalChannelUpdate(null, channelId_bc_1, channelUpdate_bc_1.shortChannelId, c, None, channelUpdate_bc_1, makeCommitments(channelId_bc_1, 49000000L)) + relayer ! LocalChannelUpdate(null, channelId_bc_1, channelUpdate_bc_1.shortChannelId, c, None, channelUpdate_bc_1, makeCommitments(channelId_bc_1, MilliSatoshi(49000000L))) sender.send(relayer, ForwardAdd(add_ab)) @@ -100,7 +100,7 @@ class RelayerSpec extends TestkitBaseClass { assert(fwd1.message.upstream === Right(add_ab)) // channel returns an error - val origin = Relayed(channelId_ab, originHtlcId = 42, amountMsatIn = 1100000, amountMsatOut = 1000000) + val origin = Relayed(channelId_ab, originHtlcId = 42, amountIn = MilliSatoshi(1100000), amountOut = MilliSatoshi(1000000)) sender.send(relayer, Status.Failure(AddHtlcFailed(channelId_bc_1, paymentHash, HtlcValueTooHighInFlight(channelId_bc_1, UInt64(1000000000L), UInt64(1516977616L)), origin, Some(channelUpdate_bc_1), originalCommand = Some(fwd1.message)))) // second try @@ -128,7 +128,7 @@ class RelayerSpec extends TestkitBaseClass { // we use this to build a valid onion val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops) // and then manually build an htlc - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) sender.send(relayer, ForwardAdd(add_ab)) @@ -148,7 +148,7 @@ class RelayerSpec extends TestkitBaseClass { // we use this to build a valid onion val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops) // and then manually build an htlc - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) @@ -174,7 +174,7 @@ class RelayerSpec extends TestkitBaseClass { // check that payments are sent properly val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops) - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) @@ -190,7 +190,7 @@ class RelayerSpec extends TestkitBaseClass { relayer ! LocalChannelDown(sender.ref, channelId = channelId_bc, shortChannelId = channelUpdate_bc.shortChannelId, remoteNodeId = TestConstants.Bob.nodeParams.nodeId) val (cmd1, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, randomBytes32, hops) - val add_ab1 = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd1.amountMsat, cmd1.paymentHash, cmd1.cltvExpiry, cmd1.onion) + val add_ab1 = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd1.amount, cmd1.paymentHash, cmd1.cltvExpiry, cmd1.onion) sender.send(relayer, ForwardAdd(add_ab1)) val fail = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message @@ -208,7 +208,7 @@ class RelayerSpec extends TestkitBaseClass { // we use this to build a valid onion val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops) // and then manually build an htlc - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) val channelUpdate_bc_disabled = channelUpdate_bc.copy(channelFlags = Announcements.makeChannelFlags(Announcements.isNode1(channelUpdate_bc.channelFlags), enable = false)) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc_disabled, makeCommitments(channelId_bc)) @@ -229,7 +229,7 @@ class RelayerSpec extends TestkitBaseClass { // we use this to build a valid onion val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops) // and then manually build an htlc with an invalid onion (hmac) - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion.copy(hmac = cmd.onion.hmac.reverse)) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion.copy(hmac = cmd.onion.hmac.reverse)) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) @@ -248,16 +248,16 @@ class RelayerSpec extends TestkitBaseClass { val sender = TestProbe() // we use this to build a valid onion - val (cmd, _) = buildCommand(UUID.randomUUID(), channelUpdate_bc.htlcMinimumMsat - 1, finalExpiry, paymentHash, hops.map(hop => hop.copy(lastUpdate = hop.lastUpdate.copy(feeBaseMsat = 0, feeProportionalMillionths = 0)))) + val (cmd, _) = buildCommand(UUID.randomUUID(), channelUpdate_bc.htlcMinimumMsat - MilliSatoshi(1), finalExpiry, paymentHash, hops.map(hop => hop.copy(lastUpdate = hop.lastUpdate.copy(feeBaseMsat = MilliSatoshi(0), feeProportionalMillionths = 0)))) // and then manually build an htlc - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) val fail = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message assert(fail.id === add_ab.id) - assert(fail.reason == Right(AmountBelowMinimum(cmd.amountMsat, channelUpdate_bc))) + assert(fail.reason == Right(AmountBelowMinimum(cmd.amount, channelUpdate_bc))) register.expectNoMsg(100 millis) paymentHandler.expectNoMsg(100 millis) @@ -270,7 +270,7 @@ class RelayerSpec extends TestkitBaseClass { val hops1 = hops.updated(1, hops(1).copy(lastUpdate = hops(1).lastUpdate.copy(cltvExpiryDelta = 0))) val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops1) // and then manually build an htlc - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) @@ -290,7 +290,7 @@ class RelayerSpec extends TestkitBaseClass { val hops1 = hops.updated(1, hops(1).copy(lastUpdate = hops(1).lastUpdate.copy(feeBaseMsat = hops(1).lastUpdate.feeBaseMsat / 2))) val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops1) // and then manually build an htlc - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) @@ -311,7 +311,7 @@ class RelayerSpec extends TestkitBaseClass { val hops1 = hops.head :: Nil val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops1) // and then manually build an htlc with a wrong expiry - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat - 1, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount - MilliSatoshi(1), cmd.paymentHash, cmd.cltvExpiry, cmd.onion) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) @@ -332,7 +332,7 @@ class RelayerSpec extends TestkitBaseClass { val hops1 = hops.head :: Nil val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops1) // and then manually build an htlc with a wrong expiry - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry - 1, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry - 1, cmd.onion) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) @@ -350,7 +350,7 @@ class RelayerSpec extends TestkitBaseClass { val sender = TestProbe() val paymentHash = randomBytes32 - val origin = Relayed(channelId_ab, originHtlcId = 42, amountMsatIn = 1100000, amountMsatOut = 1000000) + val origin = Relayed(channelId_ab, originHtlcId = 42, amountIn = MilliSatoshi(1100000), amountOut = MilliSatoshi(1000000)) sender.send(relayer, Status.Failure(AddHtlcFailed(channelId_bc, paymentHash, ExpiryTooSmall(channelId_bc, 100, 0, 0), origin, Some(channelUpdate_bc), None))) assert(register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message.reason === Right(ExpiryTooSoon(channelUpdate_bc))) @@ -358,7 +358,7 @@ class RelayerSpec extends TestkitBaseClass { sender.send(relayer, Status.Failure(AddHtlcFailed(channelId_bc, paymentHash, ExpiryTooBig(channelId_bc, 100, 200, 0), origin, Some(channelUpdate_bc), None))) assert(register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message.reason === Right(ExpiryTooFar)) - sender.send(relayer, Status.Failure(AddHtlcFailed(channelId_bc, paymentHash, InsufficientFunds(channelId_bc, origin.amountMsatOut, 100, 0, 0), origin, Some(channelUpdate_bc), None))) + sender.send(relayer, Status.Failure(AddHtlcFailed(channelId_bc, paymentHash, InsufficientFunds(channelId_bc, origin.amountOut, Satoshi(100), Satoshi(0), Satoshi(0)), origin, Some(channelUpdate_bc), None))) assert(register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message.reason === Right(TemporaryChannelFailure(channelUpdate_bc))) val channelUpdate_bc_disabled = channelUpdate_bc.copy(channelFlags = 2) @@ -383,9 +383,9 @@ class RelayerSpec extends TestkitBaseClass { system.eventStream.subscribe(eventListener.ref, classOf[PaymentEvent]) // we build a fake htlc for the downstream channel - val add_bc = UpdateAddHtlc(channelId = channelId_bc, id = 72, amountMsat = 10000000L, paymentHash = ByteVector32.Zeroes, cltvExpiry = 4200, onionRoutingPacket = TestConstants.emptyOnionPacket) + val add_bc = UpdateAddHtlc(channelId = channelId_bc, id = 72, amountMsat = MilliSatoshi(10000000L), paymentHash = ByteVector32.Zeroes, cltvExpiry = 4200, onionRoutingPacket = TestConstants.emptyOnionPacket) val fulfill_ba = UpdateFulfillHtlc(channelId = channelId_bc, id = 42, paymentPreimage = ByteVector32.Zeroes) - val origin = Relayed(channelId_ab, 150, 11000000L, 10000000L) + val origin = Relayed(channelId_ab, 150, MilliSatoshi(11000000L), MilliSatoshi(10000000L)) sender.send(relayer, ForwardFulfill(fulfill_ba, origin, add_bc)) val fwd = register.expectMsgType[Register.Forward[CMD_FULFILL_HTLC]] @@ -393,7 +393,7 @@ class RelayerSpec extends TestkitBaseClass { assert(fwd.message.id === origin.originHtlcId) val paymentRelayed = eventListener.expectMsgType[PaymentRelayed] - assert(paymentRelayed.copy(timestamp = 0) === PaymentRelayed(MilliSatoshi(origin.amountMsatIn), MilliSatoshi(origin.amountMsatOut), add_bc.paymentHash, channelId_ab, channelId_bc, timestamp = 0)) + assert(paymentRelayed.copy(timestamp = 0) === PaymentRelayed(origin.amountIn, origin.amountOut, add_bc.paymentHash, channelId_ab, channelId_bc, timestamp = 0)) } test("relay an htlc-fail") { f => @@ -401,9 +401,9 @@ class RelayerSpec extends TestkitBaseClass { val sender = TestProbe() // we build a fake htlc for the downstream channel - val add_bc = UpdateAddHtlc(channelId = channelId_bc, id = 72, amountMsat = 10000000L, paymentHash = ByteVector32.Zeroes, cltvExpiry = 4200, onionRoutingPacket = TestConstants.emptyOnionPacket) + val add_bc = UpdateAddHtlc(channelId = channelId_bc, id = 72, amountMsat = MilliSatoshi(10000000L), paymentHash = ByteVector32.Zeroes, cltvExpiry = 4200, onionRoutingPacket = TestConstants.emptyOnionPacket) val fail_ba = UpdateFailHtlc(channelId = channelId_bc, id = 42, reason = Sphinx.FailurePacket.create(ByteVector32(ByteVector.fill(32)(1)), TemporaryChannelFailure(channelUpdate_cd))) - val origin = Relayed(channelId_ab, 150, 11000000L, 10000000L) + val origin = Relayed(channelId_ab, 150, MilliSatoshi(11000000L), MilliSatoshi(10000000L)) sender.send(relayer, ForwardFail(fail_ba, origin, add_bc)) val fwd = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]] @@ -414,31 +414,31 @@ class RelayerSpec extends TestkitBaseClass { test("get usable balances") { f => import f._ val sender = TestProbe() - relayer ! LocalChannelUpdate(null, channelId_ab, channelUpdate_ab.shortChannelId, a, None, channelUpdate_ab, makeCommitments(channelId_ab, -2000, 300000)) - relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc, 400000, -5000)) + relayer ! LocalChannelUpdate(null, channelId_ab, channelUpdate_ab.shortChannelId, a, None, channelUpdate_ab, makeCommitments(channelId_ab, MilliSatoshi(-2000), MilliSatoshi(300000))) + relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc, MilliSatoshi(400000), MilliSatoshi(-5000))) sender.send(relayer, GetUsableBalances) val usableBalances1 = sender.expectMsgType[Iterable[UsableBalances]] assert(usableBalances1.size === 2) - assert(usableBalances1.head.canSendMsat === 0 && usableBalances1.head.canReceiveMsat === 300000 && usableBalances1.head.shortChannelId == channelUpdate_ab.shortChannelId) - assert(usableBalances1.last.canReceiveMsat === 0 && usableBalances1.last.canSendMsat === 400000 && usableBalances1.last.shortChannelId == channelUpdate_bc.shortChannelId) + assert(usableBalances1.head.canSend === MilliSatoshi(0) && usableBalances1.head.canReceive === MilliSatoshi(300000) && usableBalances1.head.shortChannelId == channelUpdate_ab.shortChannelId) + assert(usableBalances1.last.canReceive === MilliSatoshi(0) && usableBalances1.last.canSend === MilliSatoshi(400000) && usableBalances1.last.shortChannelId == channelUpdate_bc.shortChannelId) - relayer ! AvailableBalanceChanged(null, channelId_bc, channelUpdate_bc.shortChannelId, 0, makeCommitments(channelId_bc, 200000, 500000)) + relayer ! AvailableBalanceChanged(null, channelId_bc, channelUpdate_bc.shortChannelId, MilliSatoshi(0), makeCommitments(channelId_bc, MilliSatoshi(200000), MilliSatoshi(500000))) sender.send(relayer, GetUsableBalances) val usableBalances2 = sender.expectMsgType[Iterable[UsableBalances]] - assert(usableBalances2.last.canReceiveMsat === 500000 && usableBalances2.last.canSendMsat === 200000) + assert(usableBalances2.last.canReceive === MilliSatoshi(500000) && usableBalances2.last.canSend === MilliSatoshi(200000)) - relayer ! AvailableBalanceChanged(null, channelId_ab, channelUpdate_ab.shortChannelId, 0, makeCommitments(channelId_ab, 100000, 200000)) + relayer ! AvailableBalanceChanged(null, channelId_ab, channelUpdate_ab.shortChannelId, MilliSatoshi(0), makeCommitments(channelId_ab, MilliSatoshi(100000), MilliSatoshi(200000))) relayer ! LocalChannelDown(null, channelId_bc, channelUpdate_bc.shortChannelId, c) sender.send(relayer, GetUsableBalances) val usableBalances3 = sender.expectMsgType[Iterable[UsableBalances]] - assert(usableBalances3.size === 1 && usableBalances3.head.canSendMsat === 100000) + assert(usableBalances3.size === 1 && usableBalances3.head.canSend === MilliSatoshi(100000)) - relayer ! LocalChannelUpdate(null, channelId_ab, channelUpdate_ab.shortChannelId, a, None, channelUpdate_ab.copy(channelFlags = 2), makeCommitments(channelId_ab, 100000, 200000)) + relayer ! LocalChannelUpdate(null, channelId_ab, channelUpdate_ab.shortChannelId, a, None, channelUpdate_ab.copy(channelFlags = 2), makeCommitments(channelId_ab, MilliSatoshi(100000), MilliSatoshi(200000))) sender.send(relayer, GetUsableBalances) val usableBalances4 = sender.expectMsgType[Iterable[UsableBalances]] assert(usableBalances4.isEmpty) - relayer ! LocalChannelUpdate(null, channelId_ab, channelUpdate_ab.shortChannelId, a, None, channelUpdate_ab, makeCommitments(channelId_ab, 100000, 200000)) + relayer ! LocalChannelUpdate(null, channelId_ab, channelUpdate_ab.shortChannelId, a, None, channelUpdate_ab, makeCommitments(channelId_ab, MilliSatoshi(100000), MilliSatoshi(200000))) sender.send(relayer, GetUsableBalances) val usableBalances5 = sender.expectMsgType[Iterable[UsableBalances]] assert(usableBalances5.size === 1) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsBatchValidationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsBatchValidationSpec.scala index 0667d2a7bd..a3c29fda8e 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsBatchValidationSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsBatchValidationSpec.scala @@ -27,10 +27,9 @@ import fr.acinq.eclair.blockchain.bitcoind.BitcoinCoreWallet import fr.acinq.eclair.blockchain.bitcoind.rpc.{BasicBitcoinJsonRPCClient, ExtendedBitcoinClient} import fr.acinq.eclair.transactions.Scripts import fr.acinq.eclair.wire.{ChannelAnnouncement, ChannelUpdate} -import fr.acinq.eclair.{ShortChannelId, randomKey} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, randomKey} import org.scalatest.FunSuite import scodec.bits.ByteVector - import scala.concurrent.duration._ import scala.concurrent.{Await, ExecutionContext} @@ -104,6 +103,6 @@ object AnnouncementsBatchValidationSpec { } def makeChannelUpdate(c: SimulatedChannel, shortChannelId: ShortChannelId): ChannelUpdate = - Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, c.node1Key, c.node2Key.publicKey, shortChannelId, 10, 1000, 10, 100, 500000000L) + Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, c.node1Key, c.node2Key.publicKey, shortChannelId, 10, MilliSatoshi(1000), MilliSatoshi(10), 100, MilliSatoshi(500000000L)) } \ No newline at end of file diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsSpec.scala index d64bfab29f..41b5c722f1 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsSpec.scala @@ -16,7 +16,7 @@ package fr.acinq.eclair.router -import fr.acinq.bitcoin.Block +import fr.acinq.bitcoin.{Block} import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} import fr.acinq.eclair.TestConstants.Alice import fr.acinq.eclair._ @@ -55,7 +55,7 @@ class AnnouncementsSpec extends FunSuite { } test("create valid signed channel update announcement") { - val ann = makeChannelUpdate(Block.RegtestGenesisBlock.hash, Alice.nodeParams.privateKey, randomKey.publicKey, ShortChannelId(45561L), Alice.nodeParams.expiryDeltaBlocks, Alice.nodeParams.htlcMinimumMsat, Alice.nodeParams.feeBaseMsat, Alice.nodeParams.feeProportionalMillionth, 500000000L) + val ann = makeChannelUpdate(Block.RegtestGenesisBlock.hash, Alice.nodeParams.privateKey, randomKey.publicKey, ShortChannelId(45561L), Alice.nodeParams.expiryDeltaBlocks, Alice.nodeParams.htlcMinimum, Alice.nodeParams.feeBase, Alice.nodeParams.feeProportionalMillionth, MilliSatoshi(500000000L)) assert(checkSig(ann, Alice.nodeParams.nodeId)) assert(checkSig(ann, randomKey.publicKey) === false) } @@ -66,10 +66,10 @@ class AnnouncementsSpec extends FunSuite { // NB: node1 < node2 (public keys) assert(isNode1(node1_priv.publicKey, node2_priv.publicKey)) assert(!isNode1(node2_priv.publicKey, node1_priv.publicKey)) - val channelUpdate1 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, node1_priv, node2_priv.publicKey, ShortChannelId(0), 0, 0, 0, 0, 500000000L, enable = true) - val channelUpdate1_disabled = makeChannelUpdate(Block.RegtestGenesisBlock.hash, node1_priv, node2_priv.publicKey, ShortChannelId(0), 0, 0, 0, 0, 500000000L, enable = false) - val channelUpdate2 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, node2_priv, node1_priv.publicKey, ShortChannelId(0), 0, 0, 0, 0, 500000000L, enable = true) - val channelUpdate2_disabled = makeChannelUpdate(Block.RegtestGenesisBlock.hash, node2_priv, node1_priv.publicKey, ShortChannelId(0), 0, 0, 0, 0, 500000000L, enable = false) + val channelUpdate1 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, node1_priv, node2_priv.publicKey, ShortChannelId(0), 0, MilliSatoshi(0), MilliSatoshi(0), 0, MilliSatoshi(500000000L), enable = true) + val channelUpdate1_disabled = makeChannelUpdate(Block.RegtestGenesisBlock.hash, node1_priv, node2_priv.publicKey, ShortChannelId(0), 0, MilliSatoshi(0), MilliSatoshi(0), 0, MilliSatoshi(500000000L), enable = false) + val channelUpdate2 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, node2_priv, node1_priv.publicKey, ShortChannelId(0), 0, MilliSatoshi(0), MilliSatoshi(0), 0, MilliSatoshi(500000000L), enable = true) + val channelUpdate2_disabled = makeChannelUpdate(Block.RegtestGenesisBlock.hash, node2_priv, node1_priv.publicKey, ShortChannelId(0), 0, MilliSatoshi(0), MilliSatoshi(0), 0, MilliSatoshi(500000000L), enable = false) assert(channelUpdate1.channelFlags == 0) // ....00 assert(channelUpdate1_disabled.channelFlags == 2) // ....10 assert(channelUpdate2.channelFlags == 1) // ....01 diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/BaseRouterSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/BaseRouterSpec.scala index fc63698bb1..4acdc1db69 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/BaseRouterSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/BaseRouterSpec.scala @@ -78,14 +78,14 @@ abstract class BaseRouterSpec extends TestkitBaseClass { val chan_cd = channelAnnouncement(channelId_cd, priv_c, priv_d, priv_funding_c, priv_funding_d) val chan_ef = channelAnnouncement(channelId_ef, priv_e, priv_f, priv_funding_e, priv_funding_f) - val channelUpdate_ab = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, b, channelId_ab, cltvExpiryDelta = 7, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 10, htlcMaximumMsat = 500000000L) - val channelUpdate_ba = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, a, channelId_ab, cltvExpiryDelta = 7, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 10, htlcMaximumMsat = 500000000L) - val channelUpdate_bc = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, c, channelId_bc, cltvExpiryDelta = 5, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 1, htlcMaximumMsat = 500000000L) - val channelUpdate_cb = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_c, b, channelId_bc, cltvExpiryDelta = 5, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 1, htlcMaximumMsat = 500000000L) - val channelUpdate_cd = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_c, d, channelId_cd, cltvExpiryDelta = 3, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 4, htlcMaximumMsat = 500000000L) - val channelUpdate_dc = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_d, c, channelId_cd, cltvExpiryDelta = 3, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 4, htlcMaximumMsat = 500000000L) - val channelUpdate_ef = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_e, f, channelId_ef, cltvExpiryDelta = 9, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 8, htlcMaximumMsat = 500000000L) - val channelUpdate_fe = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_f, e, channelId_ef, cltvExpiryDelta = 9, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 8, htlcMaximumMsat = 500000000L) + val channelUpdate_ab = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, b, channelId_ab, cltvExpiryDelta = 7, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 10, htlcMaximumMsat = MilliSatoshi(500000000L)) + val channelUpdate_ba = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, a, channelId_ab, cltvExpiryDelta = 7, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 10, htlcMaximumMsat = MilliSatoshi(500000000L)) + val channelUpdate_bc = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, c, channelId_bc, cltvExpiryDelta = 5, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 1, htlcMaximumMsat = MilliSatoshi(500000000L)) + val channelUpdate_cb = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_c, b, channelId_bc, cltvExpiryDelta = 5, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 1, htlcMaximumMsat = MilliSatoshi(500000000L)) + val channelUpdate_cd = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_c, d, channelId_cd, cltvExpiryDelta = 3, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 4, htlcMaximumMsat = MilliSatoshi(500000000L)) + val channelUpdate_dc = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_d, c, channelId_cd, cltvExpiryDelta = 3, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 4, htlcMaximumMsat = MilliSatoshi(500000000L)) + val channelUpdate_ef = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_e, f, channelId_ef, cltvExpiryDelta = 9, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 8, htlcMaximumMsat = MilliSatoshi(500000000L)) + val channelUpdate_fe = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_f, e, channelId_ef, cltvExpiryDelta = 9, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 8, htlcMaximumMsat = MilliSatoshi(500000000L)) override def withFixture(test: OneArgTest): Outcome = { // the network will be a --(1)--> b ---(2)--> c --(3)--> d and e --(4)--> f (we are a) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/GraphSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/GraphSpec.scala index 85c0e2ce82..9730365850 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/GraphSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/GraphSpec.scala @@ -17,7 +17,7 @@ package fr.acinq.eclair.router import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.eclair.ShortChannelId +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId} import fr.acinq.eclair.router.Graph.GraphStructure.{DirectedGraph, GraphEdge} import fr.acinq.eclair.router.RouteCalculationSpec._ import fr.acinq.eclair.wire.ChannelUpdate @@ -46,12 +46,12 @@ class GraphSpec extends FunSuite { def makeTestGraph() = { val updates = Seq( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(3L, a, d, 0, 0), - makeUpdate(4L, d, c, 0, 0), - makeUpdate(5L, c, e, 0, 0), - makeUpdate(6L, b, e, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(3L, a, d, MilliSatoshi(0), 0), + makeUpdate(4L, d, c, MilliSatoshi(0), 0), + makeUpdate(5L, c, e, MilliSatoshi(0), 0), + makeUpdate(6L, b, e, MilliSatoshi(0), 0) ) DirectedGraph.makeGraph(updates.toMap) @@ -72,11 +72,11 @@ class GraphSpec extends FunSuite { assert(otherGraph.vertexSet().size === 5) // add some edges to the graph - val (descAB, updateAB) = makeUpdate(1L, a, b, 0, 0) - val (descBC, updateBC) = makeUpdate(2L, b, c, 0, 0) - val (descAD, updateAD) = makeUpdate(3L, a, d, 0, 0) - val (descDC, updateDC) = makeUpdate(4L, d, c, 0, 0) - val (descCE, updateCE) = makeUpdate(5L, c, e, 0, 0) + val (descAB, updateAB) = makeUpdate(1L, a, b, MilliSatoshi(0), 0) + val (descBC, updateBC) = makeUpdate(2L, b, c, MilliSatoshi(0), 0) + val (descAD, updateAD) = makeUpdate(3L, a, d, MilliSatoshi(0), 0) + val (descDC, updateDC) = makeUpdate(4L, d, c, MilliSatoshi(0), 0) + val (descCE, updateCE) = makeUpdate(5L, c, e, MilliSatoshi(0), 0) val graphWithEdges = graph .addEdge(descAB, updateAB) @@ -98,12 +98,12 @@ class GraphSpec extends FunSuite { test("instantiate a graph adding edges only") { - val edgeAB = edgeFromDesc(makeUpdate(1L, a, b, 0, 0)) - val (descBC, updateBC) = makeUpdate(2L, b, c, 0, 0) - val (descAD, updateAD) = makeUpdate(3L, a, d, 0, 0) - val (descDC, updateDC) = makeUpdate(4L, d, c, 0, 0) - val (descCE, updateCE) = makeUpdate(5L, c, e, 0, 0) - val (descBE, updateBE) = makeUpdate(6L, b, e, 0, 0) + val edgeAB = edgeFromDesc(makeUpdate(1L, a, b, MilliSatoshi(0), 0)) + val (descBC, updateBC) = makeUpdate(2L, b, c, MilliSatoshi(0), 0) + val (descAD, updateAD) = makeUpdate(3L, a, d, MilliSatoshi(0), 0) + val (descDC, updateDC) = makeUpdate(4L, d, c, MilliSatoshi(0), 0) + val (descCE, updateCE) = makeUpdate(5L, c, e, MilliSatoshi(0), 0) + val (descBE, updateBE) = makeUpdate(6L, b, e, MilliSatoshi(0), 0) val graph = DirectedGraph(edgeAB) .addEdge(descAD, updateAD) @@ -121,10 +121,10 @@ class GraphSpec extends FunSuite { test("containsEdge should return true if the graph contains that edge, false otherwise") { val updates = Seq( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(3L, c, d, 0, 0), - makeUpdate(4L, d, e, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(3L, c, d, MilliSatoshi(0), 0), + makeUpdate(4L, d, e, MilliSatoshi(0), 0) ) val graph = DirectedGraph().addEdges(updates) @@ -144,10 +144,10 @@ class GraphSpec extends FunSuite { val graph = makeTestGraph() - val (descBE, _) = makeUpdate(6L, b, e, 0, 0) - val (descCE, _) = makeUpdate(5L, c, e, 0, 0) - val (descAD, _) = makeUpdate(3L, a, d, 0, 0) - val (descDC, _) = makeUpdate(4L, d, c, 0, 0) + val (descBE, _) = makeUpdate(6L, b, e, MilliSatoshi(0), 0) + val (descCE, _) = makeUpdate(5L, c, e, MilliSatoshi(0), 0) + val (descAD, _) = makeUpdate(3L, a, d, MilliSatoshi(0), 0) + val (descDC, _) = makeUpdate(4L, d, c, MilliSatoshi(0), 0) assert(graph.edgeSet().size === 6) @@ -168,8 +168,8 @@ class GraphSpec extends FunSuite { // contains an edge A --> B val updates = Seq( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0) ) val graph = DirectedGraph().addEdges(updates) @@ -199,19 +199,19 @@ class GraphSpec extends FunSuite { assert(graph.edgesOf(a).size == 2) //now add a new edge a -> b but with a different channel update and a different ShortChannelId - val newEdgeForNewChannel = edgeFromDesc(makeUpdate(15L, a, b, 20, 0)) + val newEdgeForNewChannel = edgeFromDesc(makeUpdate(15L, a, b, MilliSatoshi(20), 0)) val mutatedGraph = graph.addEdge(newEdgeForNewChannel) assert(mutatedGraph.edgesOf(a).size == 3) //if the ShortChannelId is the same we replace the edge and the update, this edge have an update with a different 'feeBaseMsat' - val edgeForTheSameChannel = edgeFromDesc(makeUpdate(15L, a, b, 30, 0)) + val edgeForTheSameChannel = edgeFromDesc(makeUpdate(15L, a, b, MilliSatoshi(30), 0)) val mutatedGraph2 = mutatedGraph.addEdge(edgeForTheSameChannel) assert(mutatedGraph2.edgesOf(a).size == 3) // A --> B , A --> B , A --> D assert(mutatedGraph2.getEdgesBetween(a, b).size === 2) - assert(mutatedGraph2.getEdge(edgeForTheSameChannel).get.update.feeBaseMsat === 30) + assert(mutatedGraph2.getEdge(edgeForTheSameChannel).get.update.feeBaseMsat === MilliSatoshi(30)) } test("remove a vertex with incoming edges and check those edges are removed too") { @@ -234,11 +234,11 @@ class GraphSpec extends FunSuite { def edgeFromDesc(tuple: (ChannelDesc, ChannelUpdate)): GraphEdge = GraphEdge(tuple._1, tuple._2) def descFromNodes(shortChannelId: Long, a: PublicKey, b: PublicKey): ChannelDesc = { - makeUpdate(shortChannelId, a, b, 0, 0)._1 + makeUpdate(shortChannelId, a, b, MilliSatoshi(0), 0)._1 } def edgeFromNodes(shortChannelId: Long, a: PublicKey, b: PublicKey): GraphEdge = { - edgeFromDesc(makeUpdate(shortChannelId, a, b, 0, 0)) + edgeFromDesc(makeUpdate(shortChannelId, a, b, MilliSatoshi(0), 0)) } } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/RouteCalculationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/RouteCalculationSpec.scala index c09fd82df1..c4930b0bdd 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/RouteCalculationSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/RouteCalculationSpec.scala @@ -24,7 +24,7 @@ import fr.acinq.eclair.router.Graph.GraphStructure.{DirectedGraph, GraphEdge} import fr.acinq.eclair.router.Graph.{RichWeight, WeightRatios} import fr.acinq.eclair.transactions.Transactions import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{Globals, ShortChannelId, randomKey} +import fr.acinq.eclair.{Globals, MilliSatoshi, ShortChannelId, randomKey} import org.scalatest.FunSuite import scodec.bits._ @@ -43,10 +43,10 @@ class RouteCalculationSpec extends FunSuite { test("calculate simple route") { val updates = List( - makeUpdate(1L, a, b, 1, 10, cltvDelta = 1), - makeUpdate(2L, b, c, 1, 10, cltvDelta = 1), - makeUpdate(3L, c, d, 1, 10, cltvDelta = 1), - makeUpdate(4L, d, e, 1, 10, cltvDelta = 1) + makeUpdate(1L, a, b, MilliSatoshi(1), 10, cltvDelta = 1), + makeUpdate(2L, b, c, MilliSatoshi(1), 10, cltvDelta = 1), + makeUpdate(3L, c, d, MilliSatoshi(1), 10, cltvDelta = 1), + makeUpdate(4L, d, e, MilliSatoshi(1), 10, cltvDelta = 1) ).toMap val g = makeGraph(updates) @@ -75,43 +75,43 @@ class RouteCalculationSpec extends FunSuite { // cost(AE) = 10007 -> A is source, shortest path found // cost(AB) = 10009 - val amountMsat = 10000 - val expectedCost = 10007 + val amount = MilliSatoshi(10000) + val expectedCost = MilliSatoshi(10007) val updates = List( - makeUpdate(1L, a, b, feeBaseMsat = 1, feeProportionalMillionth = 200, minHtlcMsat = 0), - makeUpdate(4L, a, e, feeBaseMsat = 1, feeProportionalMillionth = 200, minHtlcMsat = 0), - makeUpdate(2L, b, c, feeBaseMsat = 1, feeProportionalMillionth = 300, minHtlcMsat = 0), - makeUpdate(3L, c, d, feeBaseMsat = 1, feeProportionalMillionth = 400, minHtlcMsat = 0), - makeUpdate(5L, e, f, feeBaseMsat = 1, feeProportionalMillionth = 400, minHtlcMsat = 0), - makeUpdate(6L, f, d, feeBaseMsat = 1, feeProportionalMillionth = 100, minHtlcMsat = 0) + makeUpdate(1L, a, b, feeBase = MilliSatoshi(1), feeProportionalMillionth = 200, minHtlc = MilliSatoshi(0)), + makeUpdate(4L, a, e, feeBase = MilliSatoshi(1), feeProportionalMillionth = 200, minHtlc = MilliSatoshi(0)), + makeUpdate(2L, b, c, feeBase = MilliSatoshi(1), feeProportionalMillionth = 300, minHtlc = MilliSatoshi(0)), + makeUpdate(3L, c, d, feeBase = MilliSatoshi(1), feeProportionalMillionth = 400, minHtlc = MilliSatoshi(0)), + makeUpdate(5L, e, f, feeBase = MilliSatoshi(1), feeProportionalMillionth = 400, minHtlc = MilliSatoshi(0)), + makeUpdate(6L, f, d, feeBase = MilliSatoshi(1), feeProportionalMillionth = 100, minHtlc = MilliSatoshi(0)) ).toMap val graph = makeGraph(updates) - val Success(route) = Router.findRoute(graph, a, d, amountMsat, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val Success(route) = Router.findRoute(graph, a, d, amount, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) - val totalCost = Graph.pathWeight(hops2Edges(route), amountMsat, false, 0, None).cost + val totalCost = Graph.pathWeight(hops2Edges(route), amount, false, 0, None).cost assert(hops2Ids(route) === 4 :: 5 :: 6 :: Nil) assert(totalCost === expectedCost) // now channel 5 could route the amount (10000) but not the amount + fees (10007) - val (desc, update) = makeUpdate(5L, e, f, feeBaseMsat = 1, feeProportionalMillionth = 400, minHtlcMsat = 0, maxHtlcMsat = Some(10005L)) + val (desc, update) = makeUpdate(5L, e, f, feeBase = MilliSatoshi(1), feeProportionalMillionth = 400, minHtlc = MilliSatoshi(0), maxHtlc = Some(MilliSatoshi(10005L))) val graph1 = graph.addEdge(desc, update) - val Success(route1) = Router.findRoute(graph1, a, d, amountMsat, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val Success(route1) = Router.findRoute(graph1, a, d, amount, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) assert(hops2Ids(route1) === 1 :: 2 :: 3 :: Nil) } test("calculate route considering the direct channel pays no fees") { val updates = List( - makeUpdate(1L, a, b, 5, 0), // a -> b - makeUpdate(2L, a, d, 15, 0), // a -> d this goes a bit closer to the target and asks for higher fees but is a direct channel - makeUpdate(3L, b, c, 5, 0), // b -> c - makeUpdate(4L, c, d, 5, 0), // c -> d - makeUpdate(5L, d, e, 5, 0) // d -> e + makeUpdate(1L, a, b, MilliSatoshi(5), 0), // a -> b + makeUpdate(2L, a, d, MilliSatoshi(15), 0), // a -> d this goes a bit closer to the target and asks for higher fees but is a direct channel + makeUpdate(3L, b, c, MilliSatoshi(5), 0), // b -> c + makeUpdate(4L, c, d, MilliSatoshi(5), 0), // c -> d + makeUpdate(5L, d, e, MilliSatoshi(5), 0) // d -> e ).toMap val g = makeGraph(updates) @@ -123,10 +123,10 @@ class RouteCalculationSpec extends FunSuite { test("calculate simple route (add and remove edges") { val updates = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(3L, c, d, 0, 0), - makeUpdate(4L, d, e, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(3L, c, d, MilliSatoshi(0), 0), + makeUpdate(4L, d, e, MilliSatoshi(0), 0) ).toMap val g = makeGraph(updates) @@ -149,10 +149,10 @@ class RouteCalculationSpec extends FunSuite { ) val updates = List( - makeUpdate(1L, f, g, 0, 0), - makeUpdate(2L, g, h, 0, 0), - makeUpdate(3L, h, i, 0, 0), - makeUpdate(4L, f, h, 50, 0) // more expensive + makeUpdate(1L, f, g, MilliSatoshi(0), 0), + makeUpdate(2L, g, h, MilliSatoshi(0), 0), + makeUpdate(3L, h, i, MilliSatoshi(0), 0), + makeUpdate(4L, f, h, MilliSatoshi(50), 0) // more expensive ).toMap val graph = makeGraph(updates) @@ -172,10 +172,10 @@ class RouteCalculationSpec extends FunSuite { ) val updates = List( - makeUpdate(1L, f, g, 0, 0), - makeUpdate(4L, f, i, 50, 0), // our starting node F has a direct channel with I - makeUpdate(2L, g, h, 0, 0), - makeUpdate(3L, h, i, 0, 0) + makeUpdate(1L, f, g, MilliSatoshi(0), 0), + makeUpdate(4L, f, i, MilliSatoshi(50), 0), // our starting node F has a direct channel with I + makeUpdate(2L, g, h, MilliSatoshi(0), 0), + makeUpdate(3L, h, i, MilliSatoshi(0), 0) ).toMap val graph = makeGraph(updates) @@ -193,10 +193,10 @@ class RouteCalculationSpec extends FunSuite { ) val updates = List( - makeUpdate(1L, f, g, 1, 0), + makeUpdate(1L, f, g, MilliSatoshi(1), 0), // the maximum htlc allowed by this channel is only 50msat greater than what we're sending - makeUpdate(2L, g, h, 1, 0, maxHtlcMsat = Some(DEFAULT_AMOUNT_MSAT + 50)), - makeUpdate(3L, h, i, 1, 0) + makeUpdate(2L, g, h, MilliSatoshi(1), 0, maxHtlc = Some(DEFAULT_AMOUNT_MSAT + MilliSatoshi(50))), + makeUpdate(3L, h, i, MilliSatoshi(1), 0) ).toMap val graph = makeGraph(updates) @@ -214,10 +214,10 @@ class RouteCalculationSpec extends FunSuite { ) val updates = List( - makeUpdate(1L, f, g, 1, 0), + makeUpdate(1L, f, g, MilliSatoshi(1), 0), // this channel requires a minimum amount that is larger than what we are sending - makeUpdate(2L, g, h, 1, 0, minHtlcMsat = DEFAULT_AMOUNT_MSAT + 50), - makeUpdate(3L, h, i, 1, 0) + makeUpdate(2L, g, h, MilliSatoshi(1), 0, minHtlc = DEFAULT_AMOUNT_MSAT + MilliSatoshi(50)), + makeUpdate(3L, h, i, MilliSatoshi(1), 0) ).toMap val graph = makeGraph(updates) @@ -236,10 +236,10 @@ class RouteCalculationSpec extends FunSuite { ) val updates = List( - makeUpdate(1L, f, g, 0, 0), - makeUpdate(2L, g, h, 5, 5), // expensive g -> h channel - makeUpdate(6L, g, h, 0, 0), // cheap g -> h channel - makeUpdate(3L, h, i, 0, 0) + makeUpdate(1L, f, g, MilliSatoshi(0), 0), + makeUpdate(2L, g, h, MilliSatoshi(5), 5), // expensive g -> h channel + makeUpdate(6L, g, h, MilliSatoshi(0), 0), // cheap g -> h channel + makeUpdate(3L, h, i, MilliSatoshi(0), 0) ).toMap val graph = makeGraph(updates) @@ -251,11 +251,11 @@ class RouteCalculationSpec extends FunSuite { test("calculate longer but cheaper route") { val updates = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(3L, c, d, 0, 0), - makeUpdate(4L, d, e, 0, 0), - makeUpdate(5L, b, e, 10, 10) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(3L, c, d, MilliSatoshi(0), 0), + makeUpdate(4L, d, e, MilliSatoshi(0), 0), + makeUpdate(5L, b, e, MilliSatoshi(10), 10) ).toMap val g = makeGraph(updates) @@ -267,8 +267,8 @@ class RouteCalculationSpec extends FunSuite { test("no local channels") { val updates = List( - makeUpdate(2L, b, c, 0, 0), - makeUpdate(4L, d, e, 0, 0) + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(4L, d, e, MilliSatoshi(0), 0) ).toMap val g = makeGraph(updates) @@ -280,9 +280,9 @@ class RouteCalculationSpec extends FunSuite { test("route not found") { val updates = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(4L, d, e, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(4L, d, e, MilliSatoshi(0), 0) ).toMap val g = makeGraph(updates) @@ -294,8 +294,8 @@ class RouteCalculationSpec extends FunSuite { test("route not found (source OR target node not connected)") { val updates = List( - makeUpdate(2L, b, c, 0, 0), - makeUpdate(4L, c, d, 0, 0) + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(4L, c, d, MilliSatoshi(0), 0) ).toMap val g = makeGraph(updates).addVertex(a).addVertex(e) @@ -310,15 +310,15 @@ class RouteCalculationSpec extends FunSuite { val lowAmount = DEFAULT_AMOUNT_MSAT / 10 val updatesHi = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0, maxHtlcMsat = Some(DEFAULT_AMOUNT_MSAT)), - makeUpdate(3L, c, d, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0, maxHtlc = Some(DEFAULT_AMOUNT_MSAT)), + makeUpdate(3L, c, d, MilliSatoshi(0), 0) ).toMap val updatesLo = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0, minHtlcMsat = DEFAULT_AMOUNT_MSAT), - makeUpdate(3L, c, d, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0, minHtlc = DEFAULT_AMOUNT_MSAT), + makeUpdate(3L, c, d, MilliSatoshi(0), 0) ).toMap val g = makeGraph(updatesHi) @@ -331,9 +331,9 @@ class RouteCalculationSpec extends FunSuite { test("route to self") { val updates = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(3L, c, d, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(3L, c, d, MilliSatoshi(0), 0) ).toMap val g = makeGraph(updates) @@ -345,10 +345,10 @@ class RouteCalculationSpec extends FunSuite { test("route to immediate neighbor") { val updates = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(3L, c, d, 0, 0), - makeUpdate(4L, d, e, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(3L, c, d, MilliSatoshi(0), 0), + makeUpdate(4L, d, e, MilliSatoshi(0), 0) ).toMap val g = makeGraph(updates) @@ -359,10 +359,10 @@ class RouteCalculationSpec extends FunSuite { test("directed graph") { val updates = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(3L, c, d, 0, 0), - makeUpdate(4L, d, e, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(3L, c, d, MilliSatoshi(0), 0), + makeUpdate(4L, d, e, MilliSatoshi(0), 0) ).toMap // a->e works, e->a fails @@ -380,14 +380,14 @@ class RouteCalculationSpec extends FunSuite { val DUMMY_SIG = Transactions.PlaceHolderSig - val uab = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(1L), 0L, 0, 0, 1, 42, 2500, 140, None) - val uba = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(1L), 1L, 0, 1, 1, 43, 2501, 141, None) - val ubc = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(2L), 1L, 0, 0, 1, 44, 2502, 142, None) - val ucb = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(2L), 1L, 0, 1, 1, 45, 2503, 143, None) - val ucd = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(3L), 1L, 1, 0, 1, 46, 2504, 144, Some(500000000L)) - val udc = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(3L), 1L, 0, 1, 1, 47, 2505, 145, None) - val ude = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(4L), 1L, 0, 0, 1, 48, 2506, 146, None) - val ued = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(4L), 1L, 0, 1, 1, 49, 2507, 147, None) + val uab = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(1L), 0L, 0, 0, 1, MilliSatoshi(42), MilliSatoshi(2500), 140, None) + val uba = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(1L), 1L, 0, 1, 1, MilliSatoshi(43), MilliSatoshi(2501), 141, None) + val ubc = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(2L), 1L, 0, 0, 1, MilliSatoshi(44), MilliSatoshi(2502), 142, None) + val ucb = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(2L), 1L, 0, 1, 1, MilliSatoshi(45), MilliSatoshi(2503), 143, None) + val ucd = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(3L), 1L, 1, 0, 1, MilliSatoshi(46), MilliSatoshi(2504), 144, Some(MilliSatoshi(500000000L))) + val udc = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(3L), 1L, 0, 1, 1, MilliSatoshi(47), MilliSatoshi(2505), 145, None) + val ude = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(4L), 1L, 0, 0, 1, MilliSatoshi(48), MilliSatoshi(2506), 146, None) + val ued = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(4L), 1L, 0, 1, 1, MilliSatoshi(49), MilliSatoshi(2507), 147, None) val updates = Map( ChannelDesc(ShortChannelId(1L), a, b) -> uab, @@ -434,10 +434,10 @@ class RouteCalculationSpec extends FunSuite { test("blacklist routes") { val updates = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(3L, c, d, 0, 0), - makeUpdate(4L, d, e, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(3L, c, d, MilliSatoshi(0), 0), + makeUpdate(4L, d, e, MilliSatoshi(0), 0) ).toMap val g = makeGraph(updates) @@ -446,7 +446,7 @@ class RouteCalculationSpec extends FunSuite { assert(route1.map(hops2Ids) === Failure(RouteNotFound)) // verify that we left the graph untouched - assert(g.containsEdge(makeUpdate(3L, c, d, 0, 0)._1)) // c -> d + assert(g.containsEdge(makeUpdate(3L, c, d, MilliSatoshi(0), 0)._1)) // c -> d assert(g.containsVertex(c)) assert(g.containsVertex(d)) @@ -457,9 +457,9 @@ class RouteCalculationSpec extends FunSuite { test("route to a destination that is not in the graph (with assisted routes)") { val updates = List( - makeUpdate(1L, a, b, 10, 10), - makeUpdate(2L, b, c, 10, 10), - makeUpdate(3L, c, d, 10, 10) + makeUpdate(1L, a, b, MilliSatoshi(10), 10), + makeUpdate(2L, b, c, MilliSatoshi(10), 10), + makeUpdate(3L, c, d, MilliSatoshi(10), 10) ).toMap val g = makeGraph(updates) @@ -468,7 +468,7 @@ class RouteCalculationSpec extends FunSuite { assert(route.map(hops2Ids) === Failure(RouteNotFound)) // now we add the missing edge to reach the destination - val (extraDesc, extraUpdate) = makeUpdate(4L, d, e, 5, 5) + val (extraDesc, extraUpdate) = makeUpdate(4L, d, e, MilliSatoshi(5), 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) @@ -478,25 +478,25 @@ class RouteCalculationSpec extends FunSuite { test("verify that extra hops takes precedence over known channels") { val updates = List( - makeUpdate(1L, a, b, 10, 10), - makeUpdate(2L, b, c, 10, 10), - makeUpdate(3L, c, d, 10, 10), - makeUpdate(4L, d, e, 10, 10) + makeUpdate(1L, a, b, MilliSatoshi(10), 10), + makeUpdate(2L, b, c, MilliSatoshi(10), 10), + makeUpdate(3L, c, d, MilliSatoshi(10), 10), + makeUpdate(4L, d, e, MilliSatoshi(10), 10) ).toMap val g = makeGraph(updates) val route1 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) assert(route1.map(hops2Ids) === Success(1 :: 2 :: 3 :: 4 :: Nil)) - assert(route1.get(1).lastUpdate.feeBaseMsat == 10) + assert(route1.get(1).lastUpdate.feeBaseMsat == MilliSatoshi(10)) - val (extraDesc, extraUpdate) = makeUpdate(2L, b, c, 5, 5) + val (extraDesc, extraUpdate) = makeUpdate(2L, b, c, MilliSatoshi(5), 5) val extraGraphEdges = Set(GraphEdge(extraDesc, extraUpdate)) val route2 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, extraEdges = extraGraphEdges, routeParams = DEFAULT_ROUTE_PARAMS) assert(route2.map(hops2Ids) === Success(1 :: 2 :: 3 :: 4 :: Nil)) - assert(route2.get(1).lastUpdate.feeBaseMsat == 5) + assert(route2.get(1).lastUpdate.feeBaseMsat == MilliSatoshi(5)) } test("compute ignored channels") { @@ -519,15 +519,15 @@ class RouteCalculationSpec extends FunSuite { ) val updates = List( - makeUpdate(1L, a, b, 10, 10), - makeUpdate(2L, b, c, 10, 10), - makeUpdate(2L, c, b, 10, 10), - makeUpdate(3L, c, d, 10, 10), - makeUpdate(4L, d, e, 10, 10), - makeUpdate(5L, f, g, 10, 10), - makeUpdate(6L, f, h, 10, 10), - makeUpdate(7L, h, i, 10, 10), - makeUpdate(8L, i, j, 10, 10) + makeUpdate(1L, a, b, MilliSatoshi(10), 10), + makeUpdate(2L, b, c, MilliSatoshi(10), 10), + makeUpdate(2L, c, b, MilliSatoshi(10), 10), + makeUpdate(3L, c, d, MilliSatoshi(10), 10), + makeUpdate(4L, d, e, MilliSatoshi(10), 10), + makeUpdate(5L, f, g, MilliSatoshi(10), 10), + makeUpdate(6L, f, h, MilliSatoshi(10), 10), + makeUpdate(7L, h, i, MilliSatoshi(10), 10), + makeUpdate(8L, i, j, MilliSatoshi(10), 10) ).toMap val ignored = Router.getIgnoredChannelDesc(updates, ignoreNodes = Set(c, j, randomKey.publicKey)) @@ -547,7 +547,7 @@ class RouteCalculationSpec extends FunSuite { val updates = nodes .zip(nodes.drop(1)) // (0, 1) :: (1, 2) :: ... .zipWithIndex // ((0, 1), 0) :: ((1, 2), 1) :: ... - .map { case ((na, nb), index) => makeUpdate(index, na, nb, 5, 0) } + .map { case ((na, nb), index) => makeUpdate(index, na, nb, MilliSatoshi(5), 0) } .toMap val g = makeGraph(updates) @@ -565,10 +565,10 @@ class RouteCalculationSpec extends FunSuite { val updates = nodes .zip(nodes.drop(1)) // (0, 1) :: (1, 2) :: ... .zipWithIndex // ((0, 1), 0) :: ((1, 2), 1) :: ... - .map { case ((na, nb), index) => makeUpdate(index, na, nb, 1, 0) } + .map { case ((na, nb), index) => makeUpdate(index, na, nb, MilliSatoshi(1), 0) } .toMap - val updates2 = updates + makeUpdate(99, nodes(2), nodes(48), 1000, 0) // expensive shorter route + val updates2 = updates + makeUpdate(99, nodes(2), nodes(48), MilliSatoshi(1000), 0) // expensive shorter route val g = makeGraph(updates2) @@ -581,12 +581,12 @@ class RouteCalculationSpec extends FunSuite { val f = randomKey.publicKey val g = makeGraph(List( - makeUpdate(1, a, b, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 50), - makeUpdate(2, b, c, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 50), - makeUpdate(3, c, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 50), - makeUpdate(4, a, e, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(5, e, f, feeBaseMsat = 5, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(6, f, d, feeBaseMsat = 5, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9) + makeUpdate(1, a, b, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 50), + makeUpdate(2, b, c, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 50), + makeUpdate(3, c, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 50), + makeUpdate(4, a, e, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(5, e, f, feeBase = MilliSatoshi(5), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(6, f, d, feeBase = MilliSatoshi(5), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9) ).toMap) val route = Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS.copy(routeMaxCltv = 28)) @@ -598,12 +598,12 @@ class RouteCalculationSpec extends FunSuite { val f = randomKey.publicKey val g = makeGraph(List( - makeUpdate(1, a, b, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(2, b, c, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(3, c, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(4, d, e, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(5, e, f, feeBaseMsat = 5, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(6, b, f, feeBaseMsat = 5, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9) + makeUpdate(1, a, b, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(2, b, c, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(3, c, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(4, d, e, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(5, e, f, feeBase = MilliSatoshi(5), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(6, b, f, feeBase = MilliSatoshi(5), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9) ).toMap) val route = Router.findRoute(g, a, f, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS.copy(routeMaxLength = 3)) @@ -613,11 +613,11 @@ class RouteCalculationSpec extends FunSuite { test("ignore loops") { val updates = List( - makeUpdate(1L, a, b, 10, 10), - makeUpdate(2L, b, c, 10, 10), - makeUpdate(3L, c, a, 10, 10), - makeUpdate(4L, c, d, 10, 10), - makeUpdate(5L, d, e, 10, 10) + makeUpdate(1L, a, b, MilliSatoshi(10), 10), + makeUpdate(2L, b, c, MilliSatoshi(10), 10), + makeUpdate(3L, c, a, MilliSatoshi(10), 10), + makeUpdate(4L, c, d, MilliSatoshi(10), 10), + makeUpdate(5L, d, e, MilliSatoshi(10), 10) ).toMap val g = makeGraph(updates) @@ -630,13 +630,13 @@ class RouteCalculationSpec extends FunSuite { // the graph contains a possible 0-cost path that goes back on its steps ( e -> f, f -> e ) val updates = List( - makeUpdate(1L, a, b, 10, 10), // a -> b - makeUpdate(2L, b, c, 10, 10), - makeUpdate(4L, c, d, 10, 10), - makeUpdate(3L, b, e, 0, 0), // b -> e - makeUpdate(6L, e, f, 0, 0), // e -> f - makeUpdate(6L, f, e, 0, 0), // e <- f - makeUpdate(5L, e, d, 0, 0) // e -> d + makeUpdate(1L, a, b, MilliSatoshi(10), 10), // a -> b + makeUpdate(2L, b, c, MilliSatoshi(10), 10), + makeUpdate(4L, c, d, MilliSatoshi(10), 10), + makeUpdate(3L, b, e, MilliSatoshi(0), 0), // b -> e + makeUpdate(6L, e, f, MilliSatoshi(0), 0), // e -> f + makeUpdate(6L, f, e, MilliSatoshi(0), 0), // e <- f + makeUpdate(5L, e, d, MilliSatoshi(0), 0) // e -> d ).toMap val g = makeGraph(updates) @@ -671,13 +671,13 @@ class RouteCalculationSpec extends FunSuite { val edges = Seq( - makeUpdate(1L, d, a, 1, 0), - makeUpdate(2L, d, e, 1, 0), - makeUpdate(3L, a, e, 1, 0), - makeUpdate(4L, e, b, 1, 0), - makeUpdate(5L, e, f, 1, 0), - makeUpdate(6L, b, c, 1, 0), - makeUpdate(7L, c, f, 1, 0) + makeUpdate(1L, d, a, MilliSatoshi(1), 0), + makeUpdate(2L, d, e, MilliSatoshi(1), 0), + makeUpdate(3L, a, e, MilliSatoshi(1), 0), + makeUpdate(4L, e, b, MilliSatoshi(1), 0), + makeUpdate(5L, e, f, MilliSatoshi(1), 0), + makeUpdate(6L, b, c, MilliSatoshi(1), 0), + makeUpdate(7L, c, f, MilliSatoshi(1), 0) ).toMap val graph = DirectedGraph.makeGraph(edges) @@ -703,15 +703,15 @@ class RouteCalculationSpec extends FunSuite { val edges = Seq( - makeUpdate(10L, c, e, 2, 0), - makeUpdate(20L, c, d, 3, 0), - makeUpdate(30L, d, f, 4, 5), // D- > F has a higher cost to distinguish it from the 2nd cheapest route - makeUpdate(40L, e, d, 1, 0), - makeUpdate(50L, e, f, 2, 0), - makeUpdate(60L, e, g, 3, 0), - makeUpdate(70L, f, g, 2, 0), - makeUpdate(80L, f, h, 1, 0), - makeUpdate(90L, g, h, 2, 0) + makeUpdate(10L, c, e, MilliSatoshi(2), 0), + makeUpdate(20L, c, d, MilliSatoshi(3), 0), + makeUpdate(30L, d, f, MilliSatoshi(4), 5), // D- > F has a higher cost to distinguish it from the 2nd cheapest route + makeUpdate(40L, e, d, MilliSatoshi(1), 0), + makeUpdate(50L, e, f, MilliSatoshi(2), 0), + makeUpdate(60L, e, g, MilliSatoshi(3), 0), + makeUpdate(70L, f, g, MilliSatoshi(2), 0), + makeUpdate(80L, f, h, MilliSatoshi(1), 0), + makeUpdate(90L, g, h, MilliSatoshi(2), 0) ) val graph = DirectedGraph().addEdges(edges) @@ -732,19 +732,19 @@ class RouteCalculationSpec extends FunSuite { // simple graph with only 2 possible paths from A to F val edges = Seq( - makeUpdate(1L, a, b, 1, 0), - makeUpdate(1L, b, a, 1, 0), - makeUpdate(2L, b, c, 1, 0), - makeUpdate(2L, c, b, 1, 0), - makeUpdate(3L, c, f, 1, 0), - makeUpdate(3L, f, c, 1, 0), - makeUpdate(4L, c, d, 1, 0), - makeUpdate(4L, d, c, 1, 0), - makeUpdate(41L, d, c, 1, 0), // there is more than one D -> C channel - makeUpdate(5L, d, e, 1, 0), - makeUpdate(5L, e, d, 1, 0), - makeUpdate(6L, e, f, 1, 0), - makeUpdate(6L, f, e, 1, 0) + makeUpdate(1L, a, b, MilliSatoshi(1), 0), + makeUpdate(1L, b, a, MilliSatoshi(1), 0), + makeUpdate(2L, b, c, MilliSatoshi(1), 0), + makeUpdate(2L, c, b, MilliSatoshi(1), 0), + makeUpdate(3L, c, f, MilliSatoshi(1), 0), + makeUpdate(3L, f, c, MilliSatoshi(1), 0), + makeUpdate(4L, c, d, MilliSatoshi(1), 0), + makeUpdate(4L, d, c, MilliSatoshi(1), 0), + makeUpdate(41L, d, c, MilliSatoshi(1), 0), // there is more than one D -> C channel + makeUpdate(5L, d, e, MilliSatoshi(1), 0), + makeUpdate(5L, e, d, MilliSatoshi(1), 0), + makeUpdate(6L, e, f, MilliSatoshi(1), 0), + makeUpdate(6L, f, e, MilliSatoshi(1), 0) ) val graph = DirectedGraph().addEdges(edges) @@ -759,19 +759,19 @@ class RouteCalculationSpec extends FunSuite { test("select a random route below the requested fee") { - val strictFeeParams = DEFAULT_ROUTE_PARAMS.copy(maxFeeBaseMsat = 7, maxFeePct = 0) + val strictFeeParams = DEFAULT_ROUTE_PARAMS.copy(maxFeeBase = MilliSatoshi(7), maxFeePct = 0) // A -> B -> C -> D has total cost of 10000005 // A -> E -> C -> D has total cost of 11080003 !! // A -> E -> F -> D has total cost of 10000006 val g = makeGraph(List( - makeUpdate(1L, a, b, feeBaseMsat = 1, 0), - makeUpdate(4L, a, e, feeBaseMsat = 1, 0), - makeUpdate(2L, b, c, feeBaseMsat = 2, 0), - makeUpdate(3L, c, d, feeBaseMsat = 3, 0), - makeUpdate(5L, e, f, feeBaseMsat = 3, 0), - makeUpdate(6L, f, d, feeBaseMsat = 3, 0), - makeUpdate(7L, e, c, feeBaseMsat = 9, 0) + makeUpdate(1L, a, b, feeBase = MilliSatoshi(1), 0), + makeUpdate(4L, a, e, feeBase = MilliSatoshi(1), 0), + makeUpdate(2L, b, c, feeBase = MilliSatoshi(2), 0), + makeUpdate(3L, c, d, feeBase = MilliSatoshi(3), 0), + makeUpdate(5L, e, f, feeBase = MilliSatoshi(3), 0), + makeUpdate(6L, f, d, feeBase = MilliSatoshi(3), 0), + makeUpdate(7L, e, c, feeBase = MilliSatoshi(9), 0) ).toMap) (for {_ <- 0 to 10} yield Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT, numRoutes = 3, routeParams = strictFeeParams)).map { @@ -781,25 +781,25 @@ class RouteCalculationSpec extends FunSuite { val routeCost = Graph.pathWeight(hops2Edges(someRoute), DEFAULT_AMOUNT_MSAT, isPartial = false, 0, None).cost - DEFAULT_AMOUNT_MSAT // over the three routes we could only get the 2 cheapest because the third is too expensive (over 7msat of fees) - assert(routeCost == 5 || routeCost == 6) + assert(routeCost == MilliSatoshi(5) || routeCost == MilliSatoshi(6)) } } test("Use weight ratios to when computing the edge weight") { - val largeCapacity = 8000000000L + val largeCapacity = MilliSatoshi(8000000000L) // A -> B -> C -> D is 'fee optimized', lower fees route (totFees = 2, totCltv = 4000) // A -> E -> F -> D is 'timeout optimized', lower CLTV route (totFees = 3, totCltv = 18) // A -> E -> C -> D is 'capacity optimized', more recent channel/larger capacity route val updates = List( - makeUpdate(1L, a, b, feeBaseMsat = 0, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 13), - makeUpdate(4L, a, e, feeBaseMsat = 0, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 12), - makeUpdate(2L, b, c, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 500), - makeUpdate(3L, c, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 500), - makeUpdate(5L, e, f, feeBaseMsat = 2, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(6L, f, d, feeBaseMsat = 2, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(7L, e, c, feeBaseMsat = 2, 0, minHtlcMsat = 0, maxHtlcMsat = Some(largeCapacity), cltvDelta = 12) + makeUpdate(1L, a, b, feeBase = MilliSatoshi(0), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 13), + makeUpdate(4L, a, e, feeBase = MilliSatoshi(0), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 12), + makeUpdate(2L, b, c, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 500), + makeUpdate(3L, c, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 500), + makeUpdate(5L, e, f, feeBase = MilliSatoshi(2), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(6L, f, d, feeBase = MilliSatoshi(2), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(7L, e, c, feeBase = MilliSatoshi(2), 0, minHtlc = MilliSatoshi(0), maxHtlc = Some(largeCapacity), cltvDelta = 12) ).toMap val g = makeGraph(updates) @@ -829,12 +829,12 @@ class RouteCalculationSpec extends FunSuite { val currentBlockHeight = 554000 val g = makeGraph(List( - makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x1"), a, b, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144), - makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x4"), a, e, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144), - makeUpdateShort(ShortChannelId(s"${currentBlockHeight - 3000}x0x2"), b, c, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144), // younger channel - makeUpdateShort(ShortChannelId(s"${currentBlockHeight - 3000}x0x3"), c, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144), - makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x5"), e, f, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144), - makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x6"), f, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144) + makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x1"), a, b, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144), + makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x4"), a, e, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144), + makeUpdateShort(ShortChannelId(s"${currentBlockHeight - 3000}x0x2"), b, c, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144), // younger channel + makeUpdateShort(ShortChannelId(s"${currentBlockHeight - 3000}x0x3"), c, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144), + makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x5"), e, f, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144), + makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x6"), f, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144) ).toMap) Globals.blockCount.set(currentBlockHeight) @@ -851,12 +851,12 @@ class RouteCalculationSpec extends FunSuite { test("prefer a route with a smaller total CLTV if fees and score are the same") { val g = makeGraph(List( - makeUpdateShort(ShortChannelId(s"0x0x1"), a, b, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 12), - makeUpdateShort(ShortChannelId(s"0x0x4"), a, e, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 12), - makeUpdateShort(ShortChannelId(s"0x0x2"), b, c, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 10), // smaller CLTV - makeUpdateShort(ShortChannelId(s"0x0x3"), c, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 12), - makeUpdateShort(ShortChannelId(s"0x0x5"), e, f, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 12), - makeUpdateShort(ShortChannelId(s"0x0x6"), f, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 12) + makeUpdateShort(ShortChannelId(s"0x0x1"), a, b, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 12), + makeUpdateShort(ShortChannelId(s"0x0x4"), a, e, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 12), + makeUpdateShort(ShortChannelId(s"0x0x2"), b, c, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 10), // smaller CLTV + makeUpdateShort(ShortChannelId(s"0x0x3"), c, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 12), + makeUpdateShort(ShortChannelId(s"0x0x5"), e, f, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 12), + makeUpdateShort(ShortChannelId(s"0x0x6"), f, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 12) ).toMap) @@ -875,12 +875,12 @@ class RouteCalculationSpec extends FunSuite { // A -> B -> C -> D is cheaper but has a total CLTV > 2016! // A -> E -> F -> D is more expensive but has a total CLTV < 2016 val g = makeGraph(List( - makeUpdateShort(ShortChannelId(s"0x0x1"), a, b, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144), - makeUpdateShort(ShortChannelId(s"0x0x4"), a, e, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144), - makeUpdateShort(ShortChannelId(s"0x0x2"), b, c, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 1000), - makeUpdateShort(ShortChannelId(s"0x0x3"), c, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 900), - makeUpdateShort(ShortChannelId(s"0x0x5"), e, f, feeBaseMsat = 10, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144), - makeUpdateShort(ShortChannelId(s"0x0x6"), f, d, feeBaseMsat = 10, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144) + makeUpdateShort(ShortChannelId(s"0x0x1"), a, b, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144), + makeUpdateShort(ShortChannelId(s"0x0x4"), a, e, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144), + makeUpdateShort(ShortChannelId(s"0x0x2"), b, c, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 1000), + makeUpdateShort(ShortChannelId(s"0x0x3"), c, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 900), + makeUpdateShort(ShortChannelId(s"0x0x5"), e, f, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144), + makeUpdateShort(ShortChannelId(s"0x0x6"), f, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144) ).toMap) val Success(routeScoreOptimized) = Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT / 2, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS.copy(ratios = Some(WeightRatios( @@ -897,22 +897,22 @@ class RouteCalculationSpec extends FunSuite { // This test have a channel (542280x2156x0) that according to heuristics is very convenient but actually useless to reach the target, // then if the cost function is not monotonic the path-finding breaks because the result path contains a loop. val updates = List( - ChannelDesc(ShortChannelId("565643x1216x0"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"024655b768ef40951b20053a5c4b951606d4d86085d51238f2c67c7dec29c792ca")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("565643x1216x0"), 0, 1.toByte, 1.toByte, 144, htlcMinimumMsat = 0, feeBaseMsat = 1000, 100, Some(15000000000L)), - ChannelDesc(ShortChannelId("565643x1216x0"), PublicKey(hex"024655b768ef40951b20053a5c4b951606d4d86085d51238f2c67c7dec29c792ca"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("565643x1216x0"), 0, 1.toByte, 0.toByte, 14, htlcMinimumMsat = 1, 1000, 10, Some(4294967295L)), - ChannelDesc(ShortChannelId("542280x2156x0"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"03cb7983dc247f9f81a0fa2dfa3ce1c255365f7279c8dd143e086ca333df10e278")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("542280x2156x0"), 0, 1.toByte, 1.toByte, 144, htlcMinimumMsat = 1000, feeBaseMsat = 1000, 100, Some(16777000000L)), - ChannelDesc(ShortChannelId("542280x2156x0"), PublicKey(hex"03cb7983dc247f9f81a0fa2dfa3ce1c255365f7279c8dd143e086ca333df10e278"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("542280x2156x0"), 0, 1.toByte, 0.toByte, 144, htlcMinimumMsat = 1, 667, 1, Some(16777000000L)), - ChannelDesc(ShortChannelId("565779x2711x0"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"036d65409c41ab7380a43448f257809e7496b52bf92057c09c4f300cbd61c50d96")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("565779x2711x0"), 0, 1.toByte, 3.toByte, 144, htlcMinimumMsat = 1, 1000, 100, Some(230000000L)), - ChannelDesc(ShortChannelId("565779x2711x0"), PublicKey(hex"036d65409c41ab7380a43448f257809e7496b52bf92057c09c4f300cbd61c50d96"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("565779x2711x0"), 0, 1.toByte, 0.toByte, 144, htlcMinimumMsat = 1, 1000, 100, Some(230000000L)) + ChannelDesc(ShortChannelId("565643x1216x0"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"024655b768ef40951b20053a5c4b951606d4d86085d51238f2c67c7dec29c792ca")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("565643x1216x0"), 0, 1.toByte, 1.toByte, 144, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(1000), 100, Some(MilliSatoshi(15000000000L))), + ChannelDesc(ShortChannelId("565643x1216x0"), PublicKey(hex"024655b768ef40951b20053a5c4b951606d4d86085d51238f2c67c7dec29c792ca"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("565643x1216x0"), 0, 1.toByte, 0.toByte, 14, htlcMinimumMsat = MilliSatoshi(1), MilliSatoshi(1000), 10, Some(MilliSatoshi(4294967295L))), + ChannelDesc(ShortChannelId("542280x2156x0"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"03cb7983dc247f9f81a0fa2dfa3ce1c255365f7279c8dd143e086ca333df10e278")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("542280x2156x0"), 0, 1.toByte, 1.toByte, 144, htlcMinimumMsat = MilliSatoshi(1000), feeBaseMsat = MilliSatoshi(1000), 100, Some(MilliSatoshi(16777000000L))), + ChannelDesc(ShortChannelId("542280x2156x0"), PublicKey(hex"03cb7983dc247f9f81a0fa2dfa3ce1c255365f7279c8dd143e086ca333df10e278"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("542280x2156x0"), 0, 1.toByte, 0.toByte, 144, htlcMinimumMsat = MilliSatoshi(1), MilliSatoshi(667), 1, Some(MilliSatoshi(16777000000L))), + ChannelDesc(ShortChannelId("565779x2711x0"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"036d65409c41ab7380a43448f257809e7496b52bf92057c09c4f300cbd61c50d96")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("565779x2711x0"), 0, 1.toByte, 3.toByte, 144, htlcMinimumMsat = MilliSatoshi(1), MilliSatoshi(1000), 100, Some(MilliSatoshi(230000000L))), + ChannelDesc(ShortChannelId("565779x2711x0"), PublicKey(hex"036d65409c41ab7380a43448f257809e7496b52bf92057c09c4f300cbd61c50d96"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("565779x2711x0"), 0, 1.toByte, 0.toByte, 144, htlcMinimumMsat = MilliSatoshi(1), MilliSatoshi(1000), 100, Some(MilliSatoshi(230000000L))) ).toMap val g = DirectedGraph.makeGraph(updates) - val params = RouteParams(randomize = false, maxFeeBaseMsat = 21000, maxFeePct = 0.03, routeMaxCltv = 1008, routeMaxLength = 6, ratios = Some( + val params = RouteParams(randomize = false, maxFeeBase = MilliSatoshi(21000), maxFeePct = 0.03, routeMaxCltv = 1008, routeMaxLength = 6, ratios = Some( WeightRatios(cltvDeltaFactor = 0.15, ageFactor = 0.35, capacityFactor = 0.5) )) val thisNode = PublicKey(hex"036d65409c41ab7380a43448f257809e7496b52bf92057c09c4f300cbd61c50d96") val targetNode = PublicKey(hex"024655b768ef40951b20053a5c4b951606d4d86085d51238f2c67c7dec29c792ca") - val amount = 351000 + val amount = MilliSatoshi(351000) Globals.blockCount.set(567634) // simulate mainnet block for heuristic val Success(route) = Router.findRoute(g, thisNode, targetNode, amount, 1, Set.empty, Set.empty, params) @@ -927,9 +927,9 @@ object RouteCalculationSpec { val noopBoundaries = { _: RichWeight => true } - val DEFAULT_AMOUNT_MSAT = 10000000 + val DEFAULT_AMOUNT_MSAT = MilliSatoshi(10000000) - val DEFAULT_ROUTE_PARAMS = RouteParams(randomize = false, maxFeeBaseMsat = 21000, maxFeePct = 0.03, routeMaxCltv = 2016, routeMaxLength = 6, ratios = None) + val DEFAULT_ROUTE_PARAMS = RouteParams(randomize = false, maxFeeBase = MilliSatoshi(21000), maxFeePct = 0.03, routeMaxCltv = 2016, routeMaxLength = 6, ratios = None) val DUMMY_SIG = Transactions.PlaceHolderSig @@ -938,26 +938,26 @@ object RouteCalculationSpec { ChannelAnnouncement(DUMMY_SIG, DUMMY_SIG, DUMMY_SIG, DUMMY_SIG, ByteVector.empty, Block.RegtestGenesisBlock.hash, ShortChannelId(shortChannelId), nodeId1, nodeId2, randomKey.publicKey, randomKey.publicKey) } - def makeUpdate(shortChannelId: Long, nodeId1: PublicKey, nodeId2: PublicKey, feeBaseMsat: Int, feeProportionalMillionth: Int, minHtlcMsat: Long = DEFAULT_AMOUNT_MSAT, maxHtlcMsat: Option[Long] = None, cltvDelta: Int = 0): (ChannelDesc, ChannelUpdate) = { - makeUpdateShort(ShortChannelId(shortChannelId), nodeId1, nodeId2, feeBaseMsat, feeProportionalMillionth, minHtlcMsat, maxHtlcMsat, cltvDelta) + def makeUpdate(shortChannelId: Long, nodeId1: PublicKey, nodeId2: PublicKey, feeBase: MilliSatoshi, feeProportionalMillionth: Int, minHtlc: MilliSatoshi = DEFAULT_AMOUNT_MSAT, maxHtlc: Option[MilliSatoshi] = None, cltvDelta: Int = 0): (ChannelDesc, ChannelUpdate) = { + makeUpdateShort(ShortChannelId(shortChannelId), nodeId1, nodeId2, feeBase, feeProportionalMillionth, minHtlc, maxHtlc, cltvDelta) } - def makeUpdateShort(shortChannelId: ShortChannelId, nodeId1: PublicKey, nodeId2: PublicKey, feeBaseMsat: Int, feeProportionalMillionth: Int, minHtlcMsat: Long = DEFAULT_AMOUNT_MSAT, maxHtlcMsat: Option[Long] = None, cltvDelta: Int = 0): (ChannelDesc, ChannelUpdate) = + def makeUpdateShort(shortChannelId: ShortChannelId, nodeId1: PublicKey, nodeId2: PublicKey, feeBase: MilliSatoshi, feeProportionalMillionth: Int, minHtlc: MilliSatoshi = DEFAULT_AMOUNT_MSAT, maxHtlc: Option[MilliSatoshi] = None, cltvDelta: Int = 0): (ChannelDesc, ChannelUpdate) = ChannelDesc(shortChannelId, nodeId1, nodeId2) -> ChannelUpdate( signature = DUMMY_SIG, chainHash = Block.RegtestGenesisBlock.hash, shortChannelId = shortChannelId, timestamp = 0L, - messageFlags = maxHtlcMsat match { + messageFlags = maxHtlc match { case Some(_) => 1 case None => 0 }, channelFlags = 0, cltvExpiryDelta = cltvDelta, - htlcMinimumMsat = minHtlcMsat, - feeBaseMsat = feeBaseMsat, + htlcMinimumMsat = minHtlc, + feeBaseMsat = feeBase, feeProportionalMillionths = feeProportionalMillionth, - htlcMaximumMsat = maxHtlcMsat + htlcMaximumMsat = maxHtlc ) def makeGraph(updates: Map[ChannelDesc, ChannelUpdate]) = DirectedGraph.makeGraph(updates) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala index 5be5540eee..c5bc27ba6b 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala @@ -30,7 +30,7 @@ import fr.acinq.eclair.router.Announcements.makeChannelUpdate import fr.acinq.eclair.router.RouteCalculationSpec.DEFAULT_AMOUNT_MSAT import fr.acinq.eclair.transactions.Scripts import fr.acinq.eclair.wire.QueryShortChannelIds -import fr.acinq.eclair.{Globals, ShortChannelId, randomKey} +import fr.acinq.eclair.{Globals, MilliSatoshi, ShortChannelId, randomKey} import scodec.bits._ import scala.collection.SortedSet @@ -52,21 +52,21 @@ class RouterSpec extends BaseRouterSpec { val channelId_ac = ShortChannelId(420000, 5, 0) val chan_ac = channelAnnouncement(channelId_ac, priv_a, priv_c, priv_funding_a, priv_funding_c) - val update_ac = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, c, channelId_ac, cltvExpiryDelta = 7, 0, feeBaseMsat = 766000, feeProportionalMillionths = 10, 500000000L) + val update_ac = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, c, channelId_ac, cltvExpiryDelta = 7, MilliSatoshi(0), feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10, MilliSatoshi(500000000L)) // a-x will not be found val priv_x = randomKey val chan_ax = channelAnnouncement(ShortChannelId(42001), priv_a, priv_x, priv_funding_a, randomKey) - val update_ax = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_x.publicKey, chan_ax.shortChannelId, cltvExpiryDelta = 7, 0, feeBaseMsat = 766000, feeProportionalMillionths = 10, 500000000L) + val update_ax = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_x.publicKey, chan_ax.shortChannelId, cltvExpiryDelta = 7, MilliSatoshi(0), feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10, MilliSatoshi(500000000L)) // a-y will have an invalid script val priv_y = randomKey val priv_funding_y = randomKey val chan_ay = channelAnnouncement(ShortChannelId(42002), priv_a, priv_y, priv_funding_a, priv_funding_y) - val update_ay = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_y.publicKey, chan_ay.shortChannelId, cltvExpiryDelta = 7, 0, feeBaseMsat = 766000, feeProportionalMillionths = 10, 500000000L) + val update_ay = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_y.publicKey, chan_ay.shortChannelId, cltvExpiryDelta = 7, MilliSatoshi(0), feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10, MilliSatoshi(500000000L)) // a-z will be spent val priv_z = randomKey val priv_funding_z = randomKey val chan_az = channelAnnouncement(ShortChannelId(42003), priv_a, priv_z, priv_funding_a, priv_funding_z) - val update_az = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_z.publicKey, chan_az.shortChannelId, cltvExpiryDelta = 7, 0, feeBaseMsat = 766000, feeProportionalMillionths = 10, 500000000L) + val update_az = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_z.publicKey, chan_az.shortChannelId, cltvExpiryDelta = 7, MilliSatoshi(0), feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10, MilliSatoshi(500000000L)) router ! PeerRoutingMessage(null, remoteNodeId, chan_ac) router ! PeerRoutingMessage(null, remoteNodeId, chan_ax) @@ -201,7 +201,7 @@ class RouterSpec extends BaseRouterSpec { assert(res.hops.map(_.nodeId).toList === a :: b :: c :: Nil) assert(res.hops.last.nextNodeId === d) - val channelUpdate_cd1 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_c, d, channelId_cd, cltvExpiryDelta = 3, 0, feeBaseMsat = 153000, feeProportionalMillionths = 4, htlcMaximumMsat = 500000000L, enable = false) + val channelUpdate_cd1 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_c, d, channelId_cd, cltvExpiryDelta = 3, MilliSatoshi(0), feeBaseMsat = MilliSatoshi(153000), feeProportionalMillionths = 4, htlcMaximumMsat = MilliSatoshi(500000000L), enable = false) sender.send(router, PeerRoutingMessage(null, remoteNodeId, channelUpdate_cd1)) sender.expectMsg(TransportHandler.ReadAck(channelUpdate_cd1)) sender.send(router, RouteRequest(a, d, DEFAULT_AMOUNT_MSAT, routeParams = relaxedRouteParams)) @@ -257,7 +257,7 @@ class RouterSpec extends BaseRouterSpec { 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 - val update = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, c, channelId, cltvExpiryDelta = 7, htlcMinimumMsat = 0, feeBaseMsat = 766000, feeProportionalMillionths = 10, htlcMaximumMsat = 5, timestamp = timestamp) + val update = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, c, channelId, cltvExpiryDelta = 7, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10, htlcMaximumMsat = MilliSatoshi(5), timestamp = timestamp) val probe = TestProbe() probe.ignoreMsg { case _: TransportHandler.ReadAck => true } probe.send(router, PeerRoutingMessage(null, remoteNodeId, announcement)) @@ -271,7 +271,7 @@ class RouterSpec extends BaseRouterSpec { val state = sender.expectMsgType[RoutingState] - val update1 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, c, channelId, cltvExpiryDelta = 7, htlcMinimumMsat = 0, feeBaseMsat = 766000, feeProportionalMillionths = 10, htlcMaximumMsat = 500000000L, timestamp = Platform.currentTime.millisecond.toSeconds) + val update1 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, c, channelId, cltvExpiryDelta = 7, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10, htlcMaximumMsat = MilliSatoshi(500000000L), timestamp = Platform.currentTime.millisecond.toSeconds) // we want to make sure that transport receives the query val transport = TestProbe() diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/RoutingSyncSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/RoutingSyncSpec.scala index 1c4d851b65..34905e88ae 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/RoutingSyncSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/RoutingSyncSpec.scala @@ -18,7 +18,7 @@ package fr.acinq.eclair.router import akka.actor.ActorSystem import akka.testkit.{TestFSMRef, TestKit, TestProbe} -import fr.acinq.bitcoin.Block +import fr.acinq.bitcoin.{Block} import fr.acinq.eclair.TestConstants.{Alice, Bob} import fr.acinq.eclair._ import fr.acinq.eclair.crypto.TransportHandler @@ -132,8 +132,8 @@ object RoutingSyncSpec { val (priv_a, priv_b, priv_funding_a, priv_funding_b) = (randomKey, randomKey, randomKey, randomKey) val channelAnn_ab = channelAnnouncement(shortChannelId, priv_a, priv_b, priv_funding_a, priv_funding_b) val TxCoordinates(blockHeight, _, _) = ShortChannelId.coordinates(shortChannelId) - val channelUpdate_ab = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_b.publicKey, shortChannelId, cltvExpiryDelta = 7, 0, feeBaseMsat = 766000, feeProportionalMillionths = 10, 500000000L, timestamp = blockHeight) - val channelUpdate_ba = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, priv_a.publicKey, shortChannelId, cltvExpiryDelta = 7, 0, feeBaseMsat = 766000, feeProportionalMillionths = 10, 500000000L, timestamp = blockHeight) + val channelUpdate_ab = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_b.publicKey, shortChannelId, cltvExpiryDelta = 7, MilliSatoshi(0), feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10, MilliSatoshi(500000000L), timestamp = blockHeight) + val channelUpdate_ba = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, priv_a.publicKey, shortChannelId, cltvExpiryDelta = 7, MilliSatoshi(0), feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10, MilliSatoshi(500000000L), timestamp = blockHeight) val nodeAnnouncement_a = makeNodeAnnouncement(priv_a, "a", Color(0, 0, 0), List()) val nodeAnnouncement_b = makeNodeAnnouncement(priv_b, "b", Color(0, 0, 0), List()) (channelAnn_ab, channelUpdate_ab, channelUpdate_ba, nodeAnnouncement_a, nodeAnnouncement_b) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/CommitmentSpecSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/CommitmentSpecSpec.scala index 1b95b50856..3df953ab0b 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/CommitmentSpecSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/CommitmentSpecSpec.scala @@ -17,53 +17,53 @@ package fr.acinq.eclair.transactions import fr.acinq.bitcoin.{ByteVector32, Crypto} -import fr.acinq.eclair.{TestConstants, randomBytes32} +import fr.acinq.eclair.{MilliSatoshi, TestConstants, randomBytes32} import fr.acinq.eclair.wire.{UpdateAddHtlc, UpdateFailHtlc, UpdateFulfillHtlc} import org.scalatest.FunSuite class CommitmentSpecSpec extends FunSuite { test("add, fulfill and fail htlcs from the sender side") { - val spec = CommitmentSpec(htlcs = Set(), feeratePerKw = 1000, toLocalMsat = 5000 * 1000, toRemoteMsat = 0) + val spec = CommitmentSpec(htlcs = Set(), feeratePerKw = 1000, toLocal = MilliSatoshi(5000000), toRemote = MilliSatoshi(0)) val R = randomBytes32 val H = Crypto.sha256(R) - val add1 = UpdateAddHtlc(ByteVector32.Zeroes, 1, 2000 * 1000, H, 400, TestConstants.emptyOnionPacket) + val add1 = UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliSatoshi(2000 * 1000), H, 400, TestConstants.emptyOnionPacket) val spec1 = CommitmentSpec.reduce(spec, add1 :: Nil, Nil) - assert(spec1 === spec.copy(htlcs = Set(DirectedHtlc(OUT, add1)), toLocalMsat = 3000 * 1000)) + assert(spec1 === spec.copy(htlcs = Set(DirectedHtlc(OUT, add1)), toLocal = MilliSatoshi(3000000))) - val add2 = UpdateAddHtlc(ByteVector32.Zeroes, 2, 1000 * 1000, H, 400, TestConstants.emptyOnionPacket) + val add2 = UpdateAddHtlc(ByteVector32.Zeroes, 2, MilliSatoshi(1000 * 1000), H, 400, TestConstants.emptyOnionPacket) val spec2 = CommitmentSpec.reduce(spec1, add2 :: Nil, Nil) - assert(spec2 === spec1.copy(htlcs = Set(DirectedHtlc(OUT, add1), DirectedHtlc(OUT, add2)), toLocalMsat = 2000 * 1000)) + assert(spec2 === spec1.copy(htlcs = Set(DirectedHtlc(OUT, add1), DirectedHtlc(OUT, add2)), toLocal = MilliSatoshi(2000000))) val ful1 = UpdateFulfillHtlc(ByteVector32.Zeroes, add1.id, R) val spec3 = CommitmentSpec.reduce(spec2, Nil, ful1 :: Nil) - assert(spec3 === spec2.copy(htlcs = Set(DirectedHtlc(OUT, add2)), toRemoteMsat = 2000 * 1000)) + assert(spec3 === spec2.copy(htlcs = Set(DirectedHtlc(OUT, add2)), toRemote = MilliSatoshi(2000000))) val fail1 = UpdateFailHtlc(ByteVector32.Zeroes, add2.id, R) val spec4 = CommitmentSpec.reduce(spec3, Nil, fail1 :: Nil) - assert(spec4 === spec3.copy(htlcs = Set(), toLocalMsat = 3000 * 1000)) + assert(spec4 === spec3.copy(htlcs = Set(), toLocal = MilliSatoshi(3000000))) } test("add, fulfill and fail htlcs from the receiver side") { - val spec = CommitmentSpec(htlcs = Set(), feeratePerKw = 1000, toLocalMsat = 0, toRemoteMsat = 5000 * 1000) + val spec = CommitmentSpec(htlcs = Set(), feeratePerKw = 1000, toLocal = MilliSatoshi(0), toRemote = MilliSatoshi(5000 * 1000)) val R = randomBytes32 val H = Crypto.sha256(R) - val add1 = UpdateAddHtlc(ByteVector32.Zeroes, 1, 2000 * 1000, H, 400, TestConstants.emptyOnionPacket) + val add1 = UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliSatoshi(2000 * 1000), H, 400, TestConstants.emptyOnionPacket) val spec1 = CommitmentSpec.reduce(spec, Nil, add1 :: Nil) - assert(spec1 === spec.copy(htlcs = Set(DirectedHtlc(IN, add1)), toRemoteMsat = 3000 * 1000)) + assert(spec1 === spec.copy(htlcs = Set(DirectedHtlc(IN, add1)), toRemote = MilliSatoshi(3000 * 1000))) - val add2 = UpdateAddHtlc(ByteVector32.Zeroes, 2, 1000 * 1000, H, 400, TestConstants.emptyOnionPacket) + val add2 = UpdateAddHtlc(ByteVector32.Zeroes, 2, MilliSatoshi(1000 * 1000), H, 400, TestConstants.emptyOnionPacket) val spec2 = CommitmentSpec.reduce(spec1, Nil, add2 :: Nil) - assert(spec2 === spec1.copy(htlcs = Set(DirectedHtlc(IN, add1), DirectedHtlc(IN, add2)), toRemoteMsat = 2000 * 1000)) + assert(spec2 === spec1.copy(htlcs = Set(DirectedHtlc(IN, add1), DirectedHtlc(IN, add2)), toRemote = MilliSatoshi(2000 * 1000))) val ful1 = UpdateFulfillHtlc(ByteVector32.Zeroes, add1.id, R) val spec3 = CommitmentSpec.reduce(spec2, ful1 :: Nil, Nil) - assert(spec3 === spec2.copy(htlcs = Set(DirectedHtlc(IN, add2)), toLocalMsat = 2000 * 1000)) + assert(spec3 === spec2.copy(htlcs = Set(DirectedHtlc(IN, add2)), toLocal = MilliSatoshi(2000 * 1000))) val fail1 = UpdateFailHtlc(ByteVector32.Zeroes, add2.id, R) val spec4 = CommitmentSpec.reduce(spec3, fail1 :: Nil, Nil) - assert(spec4 === spec3.copy(htlcs = Set(), toRemoteMsat = 3000 * 1000)) + assert(spec4 === spec3.copy(htlcs = Set(), toRemote = MilliSatoshi(3000 * 1000))) } } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TestVectorsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TestVectorsSpec.scala index 013d70b8fb..d52e1f13cc 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TestVectorsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TestVectorsSpec.scala @@ -17,8 +17,9 @@ package fr.acinq.eclair.transactions import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin._ -import fr.acinq.eclair.TestConstants +import fr.acinq.bitcoin +import fr.acinq.bitcoin.{ByteVector32, Crypto, Satoshi, Script, ScriptFlags, Transaction} +import fr.acinq.eclair.{MilliSatoshi, TestConstants} import fr.acinq.eclair.channel.Helpers.Funding import fr.acinq.eclair.crypto.Generators import fr.acinq.eclair.transactions.Transactions.{HtlcSuccessTx, HtlcTimeoutTx, TransactionWithInputInfo} @@ -154,11 +155,11 @@ class TestVectorsSpec extends FunSuite with Logging { ) val htlcs = Seq( - DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(1000000).amount, Crypto.sha256(paymentPreimages(0)), 500, TestConstants.emptyOnionPacket)), - DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(2000000).amount, Crypto.sha256(paymentPreimages(1)), 501, TestConstants.emptyOnionPacket)), - DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(2000000).amount, Crypto.sha256(paymentPreimages(2)), 502, TestConstants.emptyOnionPacket)), - DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(3000000).amount, Crypto.sha256(paymentPreimages(3)), 503, TestConstants.emptyOnionPacket)), - DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(4000000).amount, Crypto.sha256(paymentPreimages(4)), 504, TestConstants.emptyOnionPacket)) + DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(1000000), Crypto.sha256(paymentPreimages(0)), 500, TestConstants.emptyOnionPacket)), + DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(2000000), Crypto.sha256(paymentPreimages(1)), 501, TestConstants.emptyOnionPacket)), + DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(2000000), Crypto.sha256(paymentPreimages(2)), 502, TestConstants.emptyOnionPacket)), + DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(3000000), Crypto.sha256(paymentPreimages(3)), 503, TestConstants.emptyOnionPacket)), + DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(4000000), Crypto.sha256(paymentPreimages(4)), 504, TestConstants.emptyOnionPacket)) ) val htlcScripts = htlcs.map(htlc => htlc.direction match { case OUT => Scripts.htlcOffered(Local.payment_privkey.publicKey, Remote.payment_privkey.publicKey, Local.revocation_pubkey, Crypto.ripemd160(htlc.add.paymentHash)) @@ -178,8 +179,8 @@ class TestVectorsSpec extends FunSuite with Logging { } def run(spec: CommitmentSpec) = { - logger.info(s"to_local_msat: ${spec.toLocalMsat}") - logger.info(s"to_remote_msat: ${spec.toRemoteMsat}") + logger.info(s"to_local_msat: ${spec.toLocal}") + logger.info(s"to_remote_msat: ${spec.toRemote}") logger.info(s"local_feerate_per_kw: ${spec.feeratePerKw}") val commitTx = { @@ -286,7 +287,7 @@ class TestVectorsSpec extends FunSuite with Logging { test("simple commitment tx with no HTLCs") { val name = "simple commitment tx with no HTLCs" logger.info(s"name: $name") - val spec = CommitmentSpec(htlcs = Set.empty, feeratePerKw = 15000, toLocalMsat = 7000000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = Set.empty, feeratePerKw = 15000, toLocal = MilliSatoshi(7000000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 2) @@ -297,7 +298,7 @@ class TestVectorsSpec extends FunSuite with Logging { test("commitment tx with all 5 htlcs untrimmed (minimum feerate)") { val name = "commitment tx with all 5 htlcs untrimmed (minimum feerate)" logger.info(s"name: $name") - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = 0, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = 0, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 7) @@ -308,7 +309,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 7 outputs untrimmed (maximum feerate)" logger.info(s"name: $name") val feeratePerKw = 454999 / Transactions.htlcSuccessWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 7) @@ -322,7 +323,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 6 outputs untrimmed (minimum feerate)" logger.info(s"name: $name") val feeratePerKw = 454999 / Transactions.htlcSuccessWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 6) @@ -336,7 +337,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 6 outputs untrimmed (maximum feerate)" logger.info(s"name: $name") val feeratePerKw = 1454999 / Transactions.htlcSuccessWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 6) @@ -350,7 +351,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 5 outputs untrimmed (minimum feerate)" logger.info(s"name: $name") val feeratePerKw = 1454999 / Transactions.htlcSuccessWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 5) @@ -364,7 +365,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 5 outputs untrimmed (maximum feerate)" logger.info(s"name: $name") val feeratePerKw = 1454999 / Transactions.htlcTimeoutWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 5) @@ -378,7 +379,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 4 outputs untrimmed (minimum feerate)" logger.info(s"name: $name") val feeratePerKw = 1454999 / Transactions.htlcTimeoutWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 4) @@ -392,7 +393,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 4 outputs untrimmed (maximum feerate)" logger.info(s"name: $name") val feeratePerKw = 2454999 / Transactions.htlcTimeoutWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 4) @@ -406,7 +407,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 3 outputs untrimmed (minimum feerate)" logger.info(s"name: $name") val feeratePerKw = 2454999 / Transactions.htlcTimeoutWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 3) @@ -420,7 +421,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 3 outputs untrimmed (maximum feerate)" logger.info(s"name: $name") val feeratePerKw = 3454999 / Transactions.htlcSuccessWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 3) @@ -434,7 +435,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 2 outputs untrimmed (minimum feerate)" logger.info(s"name: $name") val feeratePerKw = 3454999 / Transactions.htlcSuccessWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 2) @@ -447,7 +448,7 @@ class TestVectorsSpec extends FunSuite with Logging { test("commitment tx with 2 outputs untrimmed (maximum feerate)") { val name = "commitment tx with 2 outputs untrimmed (maximum feerate)" logger.info(s"name: $name") - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = 9651180, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = 9651180, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 2) @@ -460,7 +461,7 @@ class TestVectorsSpec extends FunSuite with Logging { test("commitment tx with 1 output untrimmed (minimum feerate)") { val name = "commitment tx with 1 output untrimmed (minimum feerate)" logger.info(s"name: $name") - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = 9651181, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = 9651181, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 1) @@ -473,7 +474,7 @@ class TestVectorsSpec extends FunSuite with Logging { test("commitment tx with fee greater than funder amount") { val name = "commitment tx with fee greater than funder amount" logger.info(s"name: $name") - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = 9651936, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = 9651936, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 1) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala index f3819c8bc8..e323788552 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala @@ -20,9 +20,10 @@ import java.nio.ByteOrder import fr.acinq.bitcoin.Crypto.{PrivateKey, ripemd160, sha256} import fr.acinq.bitcoin.Script.{pay2wpkh, pay2wsh, write} -import fr.acinq.bitcoin._ +import fr.acinq.bitcoin.{Btc, ByteVector32, Crypto, MilliBtc, Protocol, Satoshi, Script, Transaction, TxOut, millibtc2satoshi} import fr.acinq.eclair.channel.Helpers.Funding -import fr.acinq.eclair.{TestConstants, randomBytes32} +import fr.acinq.eclair.{MilliSatoshi, TestConstants, randomBytes32} +import fr.acinq.eclair._ import fr.acinq.eclair.transactions.Scripts.{htlcOffered, htlcReceived, toLocalDelayed} import fr.acinq.eclair.transactions.Transactions.{addSigs, _} import fr.acinq.eclair.wire.UpdateAddHtlc @@ -63,12 +64,12 @@ class TransactionsSpec extends FunSuite with Logging { test("compute fees") { // see BOLT #3 specs val htlcs = Set( - DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(5000000).amount, ByteVector32.Zeroes, 552, TestConstants.emptyOnionPacket)), - DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(1000000).amount, ByteVector32.Zeroes, 553, TestConstants.emptyOnionPacket)), - DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(7000000).amount, ByteVector32.Zeroes, 550, TestConstants.emptyOnionPacket)), - DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(800000).amount, ByteVector32.Zeroes, 551, TestConstants.emptyOnionPacket)) + DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(5000000), ByteVector32.Zeroes, 552, TestConstants.emptyOnionPacket)), + DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(1000000), ByteVector32.Zeroes, 553, TestConstants.emptyOnionPacket)), + DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(7000000), ByteVector32.Zeroes, 550, TestConstants.emptyOnionPacket)), + DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(800000), ByteVector32.Zeroes, 551, TestConstants.emptyOnionPacket)) ) - val spec = CommitmentSpec(htlcs, feeratePerKw = 5000, toLocalMsat = 0, toRemoteMsat = 0) + val spec = CommitmentSpec(htlcs, feeratePerKw = 5000, toLocal = MilliSatoshi(0), toRemote = MilliSatoshi(0)) val fee = Transactions.commitTxFee(Satoshi(546), spec) assert(fee == Satoshi(5340)) } @@ -125,10 +126,10 @@ 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, Satoshi(20000).amount * 1000, sha256(paymentPreimage), cltvExpiry = 400144, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(20000 * 1000), sha256(paymentPreimage), cltvExpiry = 400144, 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(Satoshi(htlc.amountMsat / 1000), pubKeyScript) :: Nil, lockTime = 0) + val commitTx = Transaction(version = 0, txIn = Nil, txOut = TxOut(htlc.amountMsat.truncateToSatoshi, pubKeyScript) :: Nil, lockTime = 0) val htlcPenaltyTx = makeHtlcPenaltyTx(commitTx, outputsAlreadyUsed = Set.empty, Script.write(redeemScript), localDustLimit, finalPubKeyScript, feeratePerKw) // we use dummy signatures to compute the weight val weight = Transaction.weight(addSigs(htlcPenaltyTx, PlaceHolderSig, localRevocationPriv.publicKey).tx) @@ -140,9 +141,9 @@ 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, Satoshi(20000).amount * 1000, sha256(paymentPreimage), cltvExpiry = 400144, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(20000 * 1000), sha256(paymentPreimage), cltvExpiry = 400144, 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(Satoshi(htlc.amountMsat / 1000), pubKeyScript) :: Nil, lockTime = 0) + 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) // we use dummy signatures to compute the weight val weight = Transaction.weight(addSigs(claimHtlcSuccessTx, PlaceHolderSig, paymentPreimage).tx) @@ -154,9 +155,9 @@ 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, Satoshi(20000).amount * 1000, sha256(paymentPreimage), cltvExpiry = 400144, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(20000 * 1000), sha256(paymentPreimage), cltvExpiry = 400144, 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(Satoshi(htlc.amountMsat / 1000), pubKeyScript) :: Nil, lockTime = 0) + 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) // we use dummy signatures to compute the weight val weight = Transaction.weight(addSigs(claimClaimHtlcTimeoutTx, PlaceHolderSig).tx) @@ -183,14 +184,14 @@ class TransactionsSpec extends FunSuite with Logging { // htlc1 and htlc2 are regular IN/OUT htlcs val paymentPreimage1 = randomBytes32 - val htlc1 = UpdateAddHtlc(ByteVector32.Zeroes, 0, millibtc2satoshi(MilliBtc(100)).amount * 1000, sha256(paymentPreimage1), 300, TestConstants.emptyOnionPacket) + val htlc1 = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliBtc(100).toMilliSatoshi, sha256(paymentPreimage1), 300, TestConstants.emptyOnionPacket) val paymentPreimage2 = randomBytes32 - val htlc2 = UpdateAddHtlc(ByteVector32.Zeroes, 1, millibtc2satoshi(MilliBtc(200)).amount * 1000, sha256(paymentPreimage2), 300, TestConstants.emptyOnionPacket) + val htlc2 = UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliBtc(200).toMilliSatoshi, sha256(paymentPreimage2), 300, TestConstants.emptyOnionPacket) // htlc3 and htlc4 are dust htlcs IN/OUT htlcs, with an amount large enough to be included in the commit tx, but too small to be claimed at 2nd stage val paymentPreimage3 = randomBytes32 - val htlc3 = UpdateAddHtlc(ByteVector32.Zeroes, 2, (localDustLimit + weight2fee(feeratePerKw, htlcTimeoutWeight)).amount * 1000, sha256(paymentPreimage3), 300, TestConstants.emptyOnionPacket) + val htlc3 = UpdateAddHtlc(ByteVector32.Zeroes, 2, (localDustLimit + weight2fee(feeratePerKw, htlcTimeoutWeight)).toMilliSatoshi, sha256(paymentPreimage3), 300, TestConstants.emptyOnionPacket) val paymentPreimage4 = randomBytes32 - val htlc4 = UpdateAddHtlc(ByteVector32.Zeroes, 3, (localDustLimit + weight2fee(feeratePerKw, htlcSuccessWeight)).amount * 1000, sha256(paymentPreimage4), 300, TestConstants.emptyOnionPacket) + val htlc4 = UpdateAddHtlc(ByteVector32.Zeroes, 3, (localDustLimit + weight2fee(feeratePerKw, htlcSuccessWeight)).toMilliSatoshi, sha256(paymentPreimage4), 300, TestConstants.emptyOnionPacket) val spec = CommitmentSpec( htlcs = Set( DirectedHtlc(OUT, htlc1), @@ -199,8 +200,8 @@ class TransactionsSpec extends FunSuite with Logging { DirectedHtlc(IN, htlc4) ), feeratePerKw = feeratePerKw, - toLocalMsat = millibtc2satoshi(MilliBtc(400)).amount * 1000, - toRemoteMsat = millibtc2satoshi(MilliBtc(300)).amount * 1000) + toLocal = millibtc2satoshi(MilliBtc(400)).toMilliSatoshi, + toRemote = millibtc2satoshi(MilliBtc(300)).toMilliSatoshi) val commitTxNumber = 0x404142434445L val commitTx = { @@ -320,7 +321,7 @@ class TransactionsSpec extends FunSuite with Logging { } def htlc(direction: Direction, amount: Satoshi): DirectedHtlc = - DirectedHtlc(direction, UpdateAddHtlc(ByteVector32.Zeroes, 0, amount.amount * 1000, ByteVector32.Zeroes, 144, TestConstants.emptyOnionPacket)) + DirectedHtlc(direction, UpdateAddHtlc(ByteVector32.Zeroes, 0, amount.toMilliSatoshi, ByteVector32.Zeroes, 144, TestConstants.emptyOnionPacket)) test("BOLT 2 fee tests") { @@ -353,7 +354,7 @@ class TransactionsSpec extends FunSuite with Logging { case "received" => htlc(IN, Satoshi(amount.toLong)) } }).toSet - TestSetup(name, dustLimit, CommitmentSpec(htlcs = htlcs, feeratePerKw = feerate_per_kw.toLong, toLocalMsat = to_local_msat.toLong, toRemoteMsat = to_remote_msat.toLong), Satoshi(fee.toLong)) + TestSetup(name, dustLimit, CommitmentSpec(htlcs = htlcs, feeratePerKw = feerate_per_kw.toLong, toLocal = MilliSatoshi(to_local_msat.toLong), toRemote = MilliSatoshi(to_remote_msat.toLong)), Satoshi(fee.toLong)) }) // simple non-reg test making sure we are not missing tests diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/ChannelCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/ChannelCodecsSpec.scala index 81f6e6e0d6..c5a0af0449 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/ChannelCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/ChannelCodecsSpec.scala @@ -21,7 +21,7 @@ import java.util.UUID import akka.actor.ActorSystem import fr.acinq.bitcoin.Crypto.PrivateKey import fr.acinq.bitcoin.DeterministicWallet.KeyPath -import fr.acinq.bitcoin.{Block, ByteVector32, Crypto, DeterministicWallet, MilliSatoshi, OutPoint, Satoshi, Transaction} +import fr.acinq.bitcoin.{Block, ByteVector32, Crypto, DeterministicWallet, OutPoint, Satoshi, Transaction} import fr.acinq.eclair._ import fr.acinq.eclair.api.JsonSupport import fr.acinq.eclair.channel.Helpers.Funding @@ -84,10 +84,10 @@ class ChannelCodecsSpec extends FunSuite { val o = LocalParams( nodeId = randomKey.publicKey, channelKeyPath = DeterministicWallet.KeyPath(Seq(42L)), - dustLimitSatoshis = Random.nextInt(Int.MaxValue), + dustLimit = Satoshi(Random.nextInt(Int.MaxValue)), maxHtlcValueInFlightMsat = UInt64(Random.nextInt(Int.MaxValue)), - channelReserveSatoshis = Random.nextInt(Int.MaxValue), - htlcMinimumMsat = Random.nextInt(Int.MaxValue), + channelReserve = Satoshi(Random.nextInt(Int.MaxValue)), + htlcMinimum = MilliSatoshi(Random.nextInt(Int.MaxValue)), toSelfDelay = Random.nextInt(Short.MaxValue), maxAcceptedHtlcs = Random.nextInt(Short.MaxValue), defaultFinalScriptPubKey = randomBytes(10 + Random.nextInt(200)), @@ -102,10 +102,10 @@ class ChannelCodecsSpec extends FunSuite { test("encode/decode remoteparams") { val o = RemoteParams( nodeId = randomKey.publicKey, - dustLimitSatoshis = Random.nextInt(Int.MaxValue), + dustLimit = Satoshi(Random.nextInt(Int.MaxValue)), maxHtlcValueInFlightMsat = UInt64(Random.nextInt(Int.MaxValue)), - channelReserveSatoshis = Random.nextInt(Int.MaxValue), - htlcMinimumMsat = Random.nextInt(Int.MaxValue), + channelReserve = Satoshi(Random.nextInt(Int.MaxValue)), + htlcMinimum = MilliSatoshi(Random.nextInt(Int.MaxValue)), toSelfDelay = Random.nextInt(Short.MaxValue), maxAcceptedHtlcs = Random.nextInt(Short.MaxValue), fundingPubKey = randomKey.publicKey, @@ -129,7 +129,7 @@ class ChannelCodecsSpec extends FunSuite { val add = UpdateAddHtlc( channelId = randomBytes32, id = Random.nextInt(Int.MaxValue), - amountMsat = Random.nextInt(Int.MaxValue), + amountMsat = MilliSatoshi(Random.nextInt(Int.MaxValue)), cltvExpiry = Random.nextInt(Int.MaxValue), paymentHash = randomBytes32, onionRoutingPacket = TestConstants.emptyOnionPacket) @@ -143,14 +143,14 @@ class ChannelCodecsSpec extends FunSuite { val add1 = UpdateAddHtlc( channelId = randomBytes32, id = Random.nextInt(Int.MaxValue), - amountMsat = Random.nextInt(Int.MaxValue), + amountMsat = MilliSatoshi(Random.nextInt(Int.MaxValue)), cltvExpiry = Random.nextInt(Int.MaxValue), paymentHash = randomBytes32, onionRoutingPacket = TestConstants.emptyOnionPacket) val add2 = UpdateAddHtlc( channelId = randomBytes32, id = Random.nextInt(Int.MaxValue), - amountMsat = Random.nextInt(Int.MaxValue), + amountMsat = MilliSatoshi(Random.nextInt(Int.MaxValue)), cltvExpiry = Random.nextInt(Int.MaxValue), paymentHash = randomBytes32, onionRoutingPacket = TestConstants.emptyOnionPacket) @@ -161,8 +161,8 @@ class ChannelCodecsSpec extends FunSuite { val o = CommitmentSpec( htlcs = Set(htlc1, htlc2), feeratePerKw = Random.nextInt(Int.MaxValue), - toLocalMsat = Random.nextInt(Int.MaxValue), - toRemoteMsat = Random.nextInt(Int.MaxValue) + toLocal = MilliSatoshi(Random.nextInt(Int.MaxValue)), + toRemote = MilliSatoshi(Random.nextInt(Int.MaxValue)) ) val encoded = commitmentSpecCodec.encode(o).require val decoded = commitmentSpecCodec.decode(encoded).require @@ -173,17 +173,17 @@ class ChannelCodecsSpec extends FunSuite { val id = UUID.randomUUID() assert(originCodec.decodeValue(originCodec.encode(Local(id, Some(ActorSystem("system").deadLetters))).require).require === Local(id, None)) // TODO: add backward compatibility check - val relayed = Relayed(randomBytes32, 4324, 12000000L, 11000000L) + val relayed = Relayed(randomBytes32, 4324, MilliSatoshi(12000000L), MilliSatoshi(11000000L)) assert(originCodec.decodeValue(originCodec.encode(relayed).require).require === relayed) } test("encode/decode map of origins") { val map = Map( 1L -> Local(UUID.randomUUID(), None), - 42L -> Relayed(randomBytes32, 4324, 12000000L, 11000000L), - 130L -> Relayed(randomBytes32, -45, 13000000L, 12000000L), - 1000L -> Relayed(randomBytes32, 10, 14000000L, 13000000L), - -32L -> Relayed(randomBytes32, 54, 15000000L, 14000000L), + 42L -> Relayed(randomBytes32, 4324, MilliSatoshi(12000000L), MilliSatoshi(11000000L)), + 130L -> Relayed(randomBytes32, -45, MilliSatoshi(13000000L), MilliSatoshi(12000000L)), + 1000L -> Relayed(randomBytes32, 10, MilliSatoshi(14000000L), MilliSatoshi(13000000L)), + -32L -> Relayed(randomBytes32, 54, MilliSatoshi(15000000L), MilliSatoshi(14000000L)), -4L -> Local(UUID.randomUUID(), None)) assert(originsMapCodec.decodeValue(originsMapCodec.encode(map).require).require === map) } @@ -313,8 +313,24 @@ class ChannelCodecsSpec extends FunSuite { // and we decode with the new codec val newnormal = stateDataCodec.decode(newbin.bits).require.value // finally we check that the actual data is the same as before (we just remove the new json field) - val oldjson = Serialization.write(oldnormal)(JsonSupport.formats).replace(""","unknownFields":""""", "").replace(""""channelVersion":"00000000000000000000000000000000",""", "") - val newjson = Serialization.write(newnormal)(JsonSupport.formats).replace(""","unknownFields":""""", "").replace(""""channelVersion":"00000000000000000000000000000000",""", "") + val oldjson = Serialization.write(oldnormal)(JsonSupport.formats) + .replace(""","unknownFields":""""", "") + .replace(""""channelVersion":"00000000000000000000000000000000",""", "") + .replace(""""dustLimit"""", """"dustLimitSatoshis"""") + .replace(""""channelReserve"""", """"channelReserveSatoshis"""") + .replace(""""htlcMinimum"""", """"htlcMinimumMsat"""") + .replace(""""toLocal"""", """"toLocalMsat"""") + .replace(""""toRemote"""", """"toRemoteMsat"""") + + val newjson = Serialization.write(newnormal)(JsonSupport.formats) + .replace(""","unknownFields":""""", "") + .replace(""""channelVersion":"00000000000000000000000000000000",""", "") + .replace(""""dustLimit"""", """"dustLimitSatoshis"""") + .replace(""""channelReserve"""", """"channelReserveSatoshis"""") + .replace(""""htlcMinimum"""", """"htlcMinimumMsat"""") + .replace(""""toLocal"""", """"toLocalMsat"""") + .replace(""""toRemote"""", """"toRemoteMsat"""") + assert(oldjson === refjson) assert(newjson === refjson) } @@ -328,10 +344,10 @@ object ChannelCodecsSpec { val localParams = LocalParams( keyManager.nodeId, channelKeyPath = DeterministicWallet.KeyPath(Seq(42L)), - dustLimitSatoshis = Satoshi(546).toLong, + dustLimit = Satoshi(546), maxHtlcValueInFlightMsat = UInt64(50000000), - channelReserveSatoshis = 10000, - htlcMinimumMsat = 10000, + channelReserve = Satoshi(10000), + htlcMinimum = MilliSatoshi(10000), toSelfDelay = 144, maxAcceptedHtlcs = 50, defaultFinalScriptPubKey = ByteVector.empty, @@ -341,10 +357,10 @@ object ChannelCodecsSpec { val remoteParams = RemoteParams( nodeId = randomKey.publicKey, - dustLimitSatoshis = Satoshi(546).toLong, + dustLimit = Satoshi(546), maxHtlcValueInFlightMsat = UInt64(5000000), - channelReserveSatoshis = 10000, - htlcMinimumMsat = 5000, + channelReserve = Satoshi(10000), + htlcMinimum = MilliSatoshi(5000), toSelfDelay = 144, maxAcceptedHtlcs = 50, fundingPubKey = PrivateKey(ByteVector32(ByteVector.fill(32)(1)) :+ 1.toByte).publicKey, @@ -364,27 +380,27 @@ object ChannelCodecsSpec { ) val htlcs = Seq( - DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(1000000).amount, Crypto.sha256(paymentPreimages(0)), 500, TestConstants.emptyOnionPacket)), - DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliSatoshi(2000000).amount, Crypto.sha256(paymentPreimages(1)), 501, TestConstants.emptyOnionPacket)), - DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 30, MilliSatoshi(2000000).amount, Crypto.sha256(paymentPreimages(2)), 502, TestConstants.emptyOnionPacket)), - DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 31, MilliSatoshi(3000000).amount, Crypto.sha256(paymentPreimages(3)), 503, TestConstants.emptyOnionPacket)), - DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 2, MilliSatoshi(4000000).amount, Crypto.sha256(paymentPreimages(4)), 504, TestConstants.emptyOnionPacket)) + DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(1000000), Crypto.sha256(paymentPreimages(0)), 500, TestConstants.emptyOnionPacket)), + DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliSatoshi(2000000), Crypto.sha256(paymentPreimages(1)), 501, TestConstants.emptyOnionPacket)), + DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 30, MilliSatoshi(2000000), Crypto.sha256(paymentPreimages(2)), 502, TestConstants.emptyOnionPacket)), + DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 31, MilliSatoshi(3000000), Crypto.sha256(paymentPreimages(3)), 503, TestConstants.emptyOnionPacket)), + DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 2, MilliSatoshi(4000000), Crypto.sha256(paymentPreimages(4)), 504, TestConstants.emptyOnionPacket)) ) val fundingTx = Transaction.read("0200000001adbb20ea41a8423ea937e76e8151636bf6093b70eaff942930d20576600521fd000000006b48304502210090587b6201e166ad6af0227d3036a9454223d49a1f11839c1a362184340ef0240220577f7cd5cca78719405cbf1de7414ac027f0239ef6e214c90fcaab0454d84b3b012103535b32d5eb0a6ed0982a0479bbadc9868d9836f6ba94dd5a63be16d875069184ffffffff028096980000000000220020c015c4a6be010e21657068fc2e6a9d02b27ebe4d490a25846f7237f104d1a3cd20256d29010000001600143ca33c2e4446f4a305f23c80df8ad1afdcf652f900000000") val fundingAmount = fundingTx.txOut(0).amount val commitmentInput = Funding.makeFundingInputInfo(fundingTx.hash, 0, fundingAmount, keyManager.fundingPublicKey(localParams.channelKeyPath).publicKey, remoteParams.fundingPubKey) - val localCommit = LocalCommit(0, CommitmentSpec(htlcs.toSet, 1500, 50000000, 70000000), PublishableTxs(CommitTx(commitmentInput, Transaction(2, Nil, Nil, 0)), Nil)) - val remoteCommit = RemoteCommit(0, CommitmentSpec(htlcs.map(htlc => htlc.copy(direction = htlc.direction.opposite)).toSet, 1500, 50000, 700000), ByteVector32(hex"0303030303030303030303030303030303030303030303030303030303030303"), PrivateKey(ByteVector.fill(32)(4)).publicKey) + val localCommit = LocalCommit(0, CommitmentSpec(htlcs.toSet, 1500, MilliSatoshi(50000000), MilliSatoshi(70000000)), PublishableTxs(CommitTx(commitmentInput, Transaction(2, Nil, Nil, 0)), Nil)) + val remoteCommit = RemoteCommit(0, CommitmentSpec(htlcs.map(htlc => htlc.copy(direction = htlc.direction.opposite)).toSet, 1500, MilliSatoshi(50000), MilliSatoshi(700000)), ByteVector32(hex"0303030303030303030303030303030303030303030303030303030303030303"), PrivateKey(ByteVector.fill(32)(4)).publicKey) val commitments = Commitments(ChannelVersion.STANDARD, localParams, remoteParams, channelFlags = 0x01.toByte, localCommit, remoteCommit, LocalChanges(Nil, Nil, Nil), RemoteChanges(Nil, Nil, Nil), localNextHtlcId = 32L, remoteNextHtlcId = 4L, - originChannels = Map(42L -> Local(UUID.randomUUID, None), 15000L -> Relayed(ByteVector32(ByteVector.fill(32)(42)), 43, 11000000L, 10000000L)), + originChannels = Map(42L -> Local(UUID.randomUUID, None), 15000L -> Relayed(ByteVector32(ByteVector.fill(32)(42)), 43, MilliSatoshi(11000000L), MilliSatoshi(10000000L))), remoteNextCommitInfo = Right(randomKey.publicKey), commitInput = commitmentInput, remotePerCommitmentSecrets = ShaChain.init, channelId = ByteVector32.Zeroes) - val channelUpdate = Announcements.makeChannelUpdate(ByteVector32(ByteVector.fill(32)(1)), randomKey, randomKey.publicKey, ShortChannelId(142553), 42, 15, 575, 53, Channel.MAX_FUNDING_SATOSHIS * 1000L) + val channelUpdate = Announcements.makeChannelUpdate(ByteVector32(ByteVector.fill(32)(1)), randomKey, randomKey.publicKey, ShortChannelId(142553), 42, MilliSatoshi(15), MilliSatoshi(575), 53, Channel.MAX_FUNDING.toMilliSatoshi) val normal = DATA_NORMAL(commitments, ShortChannelId(42), true, None, channelUpdate, None, None) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/FailureMessageCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/FailureMessageCodecsSpec.scala index 3b3ecd7a61..7f1f2ef22f 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/FailureMessageCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/FailureMessageCodecsSpec.scala @@ -18,7 +18,7 @@ package fr.acinq.eclair.wire import fr.acinq.bitcoin.{Block, ByteVector32, ByteVector64} import fr.acinq.eclair.crypto.Hmac256 -import fr.acinq.eclair.{ShortChannelId, randomBytes32, randomBytes64} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, randomBytes32, randomBytes64} import fr.acinq.eclair.wire.FailureMessageCodecs._ import org.scalatest.FunSuite import scodec.bits._ @@ -36,8 +36,8 @@ class FailureMessageCodecsSpec extends FunSuite { cltvExpiryDelta = 100, messageFlags = 0, channelFlags = 1, - htlcMinimumMsat = 1000, - feeBaseMsat = 12, + htlcMinimumMsat = MilliSatoshi(1000), + feeBaseMsat = MilliSatoshi(12), feeProportionalMillionths = 76, htlcMaximumMsat = None) @@ -46,8 +46,8 @@ class FailureMessageCodecsSpec extends FunSuite { InvalidRealm :: TemporaryNodeFailure :: PermanentNodeFailure :: RequiredNodeFeatureMissing :: InvalidOnionVersion(randomBytes32) :: InvalidOnionHmac(randomBytes32) :: InvalidOnionKey(randomBytes32) :: InvalidOnionPayload(randomBytes32) :: TemporaryChannelFailure(channelUpdate) :: PermanentChannelFailure :: RequiredChannelFeatureMissing :: UnknownNextPeer :: - AmountBelowMinimum(123456, channelUpdate) :: FeeInsufficient(546463, channelUpdate) :: IncorrectCltvExpiry(1211, channelUpdate) :: ExpiryTooSoon(channelUpdate) :: - IncorrectOrUnknownPaymentDetails(123456L) :: IncorrectPaymentAmount :: FinalExpiryTooSoon :: FinalIncorrectCltvExpiry(1234) :: ChannelDisabled(0, 1, channelUpdate) :: ExpiryTooFar :: Nil + AmountBelowMinimum(MilliSatoshi(123456), channelUpdate) :: FeeInsufficient(MilliSatoshi(546463), channelUpdate) :: IncorrectCltvExpiry(1211, channelUpdate) :: ExpiryTooSoon(channelUpdate) :: + IncorrectOrUnknownPaymentDetails(MilliSatoshi(123456L)) :: IncorrectPaymentAmount :: FinalExpiryTooSoon :: FinalIncorrectCltvExpiry(1234) :: ChannelDisabled(0, 1, channelUpdate) :: ExpiryTooFar :: Nil msgs.foreach { msg => { @@ -75,7 +75,7 @@ class FailureMessageCodecsSpec extends FunSuite { val codec = failureOnionCodec(Hmac256(ByteVector32.Zeroes)) val testCases = Map( InvalidOnionKey(ByteVector32(hex"2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a")) -> hex"41a824e2d630111669fa3e52b600a518f369691909b4e89205dc624ee17ed2c1 0022 c006 2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a 00de 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - IncorrectOrUnknownPaymentDetails(42) -> hex"ba6e122b2941619e2106e8437bf525356ffc8439ac3b2245f68546e298a08cc6 000a 400f 000000000000002a 00f6 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + IncorrectOrUnknownPaymentDetails(MilliSatoshi(42)) -> hex"ba6e122b2941619e2106e8437bf525356ffc8439ac3b2245f68546e298a08cc6 000a 400f 000000000000002a 00f6 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ) for ((expected, bin) <- testCases) { @@ -112,7 +112,7 @@ class FailureMessageCodecsSpec extends FunSuite { test("support encoding of channel_update with/without type in failure messages") { val tmp_channel_failure_notype = hex"10070080cc3e80149073ed487c76e48e9622bf980f78267b8a34a3f61921f2d8fce6063b08e74f34a073a13f2097337e4915bb4c001f3b5c4d81e9524ed575e1f45782196fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000008260500041300005b91b52f0003000e00000000000003e80000000100000001" val tmp_channel_failure_withtype = hex"100700820102cc3e80149073ed487c76e48e9622bf980f78267b8a34a3f61921f2d8fce6063b08e74f34a073a13f2097337e4915bb4c001f3b5c4d81e9524ed575e1f45782196fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000008260500041300005b91b52f0003000e00000000000003e80000000100000001" - val ref = TemporaryChannelFailure(ChannelUpdate(ByteVector64(hex"cc3e80149073ed487c76e48e9622bf980f78267b8a34a3f61921f2d8fce6063b08e74f34a073a13f2097337e4915bb4c001f3b5c4d81e9524ed575e1f4578219"), Block.LivenetGenesisBlock.hash, ShortChannelId(0x826050004130000L), 1536275759, 0, 3, 14, 1000, 1, 1, None)) + val ref = TemporaryChannelFailure(ChannelUpdate(ByteVector64(hex"cc3e80149073ed487c76e48e9622bf980f78267b8a34a3f61921f2d8fce6063b08e74f34a073a13f2097337e4915bb4c001f3b5c4d81e9524ed575e1f4578219"), Block.LivenetGenesisBlock.hash, ShortChannelId(0x826050004130000L), 1536275759, 0, 3, 14, MilliSatoshi(1000), MilliSatoshi(1), 1, None)) val u = failureMessageCodec.decode(tmp_channel_failure_notype.toBitVector).require.value assert(u === ref) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/LightningMessageCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/LightningMessageCodecsSpec.scala index ff2c26b5a2..90edaabaee 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/LightningMessageCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/LightningMessageCodecsSpec.scala @@ -19,7 +19,7 @@ package fr.acinq.eclair.wire import java.net.{Inet4Address, InetAddress} import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin.{Block, ByteVector32, ByteVector64} +import fr.acinq.bitcoin.{Block, ByteVector32, ByteVector64, Satoshi} import fr.acinq.eclair._ import fr.acinq.eclair.router.Announcements import fr.acinq.eclair.wire.LightningMessageCodecs._ @@ -52,15 +52,15 @@ class LightningMessageCodecsSpec extends FunSuite { } test("encode/decode all channel messages") { - val open = OpenChannel(randomBytes32, randomBytes32, 3, 4, 5, UInt64(6), 7, 8, 9, 10, 11, publicKey(1), point(2), point(3), point(4), point(5), point(6), 0.toByte) - val accept = AcceptChannel(randomBytes32, 3, UInt64(4), 5, 6, 7, 8, 9, publicKey(1), point(2), point(3), point(4), point(5), point(6)) + val open = OpenChannel(randomBytes32, randomBytes32, Satoshi(3), MilliSatoshi(4), Satoshi(5), UInt64(6), Satoshi(7), MilliSatoshi(8), 9, 10, 11, publicKey(1), point(2), point(3), point(4), point(5), point(6), 0.toByte) + val accept = AcceptChannel(randomBytes32, Satoshi(3), UInt64(4), Satoshi(5), MilliSatoshi(6), 7, 8, 9, publicKey(1), point(2), point(3), point(4), point(5), point(6)) val funding_created = FundingCreated(randomBytes32, bin32(0), 3, randomBytes64) val funding_signed = FundingSigned(randomBytes32, randomBytes64) val funding_locked = FundingLocked(randomBytes32, point(2)) val update_fee = UpdateFee(randomBytes32, 2) val shutdown = Shutdown(randomBytes32, bin(47, 0)) - val closing_signed = ClosingSigned(randomBytes32, 2, randomBytes64) - val update_add_htlc = UpdateAddHtlc(randomBytes32, 2, 3, bin32(0), 4, TestConstants.emptyOnionPacket) + val closing_signed = ClosingSigned(randomBytes32, Satoshi(2), randomBytes64) + val update_add_htlc = UpdateAddHtlc(randomBytes32, 2, MilliSatoshi(3), bin32(0), 4, TestConstants.emptyOnionPacket) val update_fulfill_htlc = UpdateFulfillHtlc(randomBytes32, 2, bin32(0)) val update_fail_htlc = UpdateFailHtlc(randomBytes32, 2, bin(154, 0)) val update_fail_malformed_htlc = UpdateFailMalformedHtlc(randomBytes32, 2, randomBytes32, 1111) @@ -68,7 +68,7 @@ class LightningMessageCodecsSpec extends FunSuite { val revoke_and_ack = RevokeAndAck(randomBytes32, scalar(0), point(1)) val channel_announcement = ChannelAnnouncement(randomBytes64, randomBytes64, randomBytes64, randomBytes64, bin(7, 9), Block.RegtestGenesisBlock.hash, ShortChannelId(1), randomKey.publicKey, randomKey.publicKey, randomKey.publicKey, randomKey.publicKey) val node_announcement = NodeAnnouncement(randomBytes64, bin(1, 2), 1, randomKey.publicKey, Color(100.toByte, 200.toByte, 300.toByte), "node-alias", IPv4(InetAddress.getByAddress(Array[Byte](192.toByte, 168.toByte, 1.toByte, 42.toByte)).asInstanceOf[Inet4Address], 42000) :: Nil) - val channel_update = ChannelUpdate(randomBytes64, Block.RegtestGenesisBlock.hash, ShortChannelId(1), 2, 42, 0, 3, 4, 5, 6, None) + val channel_update = ChannelUpdate(randomBytes64, Block.RegtestGenesisBlock.hash, ShortChannelId(1), 2, 42, 0, 3, MilliSatoshi(4), MilliSatoshi(5), 6, None) val announcement_signatures = AnnouncementSignatures(randomBytes32, ShortChannelId(42), randomBytes64, randomBytes64) val gossip_timestamp_filter = GossipTimestampFilter(Block.RegtestGenesisBlock.blockId, 100000, 1500) val query_short_channel_id = QueryShortChannelIds(Block.RegtestGenesisBlock.blockId, randomBytes(7515)) @@ -96,7 +96,7 @@ class LightningMessageCodecsSpec extends FunSuite { // this was generated by c-lightning val bin = hex"010258fff7d0e987e2cdd560e3bb5a046b4efe7b26c969c2f51da1dceec7bcb8ae1b634790503d5290c1a6c51d681cf8f4211d27ed33a257dcc1102862571bf1792306226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f0005a100000200005bc75919010100060000000000000001000000010000000a000000003a699d00" val update = lightningMessageCodec.decode(bin.bits).require.value.asInstanceOf[ChannelUpdate] - assert(update === ChannelUpdate(ByteVector64(hex"58fff7d0e987e2cdd560e3bb5a046b4efe7b26c969c2f51da1dceec7bcb8ae1b634790503d5290c1a6c51d681cf8f4211d27ed33a257dcc1102862571bf17923"), ByteVector32(hex"06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f"), ShortChannelId(0x5a10000020000L), 1539791129, 1, 1, 6, 1, 1, 10, Some(980000000L))) + assert(update === ChannelUpdate(ByteVector64(hex"58fff7d0e987e2cdd560e3bb5a046b4efe7b26c969c2f51da1dceec7bcb8ae1b634790503d5290c1a6c51d681cf8f4211d27ed33a257dcc1102862571bf17923"), ByteVector32(hex"06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f"), ShortChannelId(0x5a10000020000L), 1539791129, 1, 1, 6, MilliSatoshi(1), MilliSatoshi(1), 10, Some(MilliSatoshi(980000000L)))) val nodeId = PublicKey(hex"03370c9bac836e557eb4f017fe8f9cc047f44db39c1c4e410ff0f7be142b817ae4") assert(Announcements.checkSig(update, nodeId)) val bin2 = ByteVector(lightningMessageCodec.encode(update).require.toByteArray) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/OnionCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/OnionCodecsSpec.scala index 763a9ce42a..bf236ed7b7 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/OnionCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/OnionCodecsSpec.scala @@ -17,7 +17,7 @@ package fr.acinq.eclair.wire import fr.acinq.bitcoin.ByteVector32 -import fr.acinq.eclair.ShortChannelId +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId} import fr.acinq.eclair.wire.OnionCodecs._ import org.scalatest.FunSuite import scodec.bits.HexStringSyntax @@ -40,7 +40,7 @@ class OnionCodecsSpec extends FunSuite { } test("encode/decode per-hop payload") { - val payload = PerHopPayload(shortChannelId = ShortChannelId(42), amtToForward = 142000, outgoingCltvValue = 500000) + val payload = PerHopPayload(shortChannelId = ShortChannelId(42), amtToForward = MilliSatoshi(142000), outgoingCltvValue = 500000) val bin = perHopPayloadCodec.encode(payload).require assert(bin.toByteVector.size === 33) val payload1 = perHopPayloadCodec.decode(bin).require.value diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/GUIUpdater.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/GUIUpdater.scala index ed96e755f0..1f955e9d4b 100644 --- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/GUIUpdater.scala +++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/GUIUpdater.scala @@ -21,7 +21,7 @@ import java.time.LocalDateTime import akka.actor.{Actor, ActorLogging, ActorRef, Terminated} import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.bitcoin._ -import fr.acinq.eclair.CoinUtils +import fr.acinq.eclair.{CoinUtils, MilliSatoshi} import fr.acinq.eclair.blockchain.bitcoind.zmq.ZMQActor.{ZMQConnected, ZMQDisconnected} import fr.acinq.eclair.blockchain.electrum.ElectrumClient.{ElectrumDisconnected, ElectrumReady} import fr.acinq.eclair.channel._ @@ -187,11 +187,11 @@ class GUIUpdater(mainController: MainController) extends Actor with ActorLogging val c = mainController.networkChannelsMap.get(channelUpdate.shortChannelId) if (Announcements.isNode1(channelUpdate.channelFlags)) { c.isNode1Enabled = Some(Announcements.isEnabled(channelUpdate.channelFlags)) - c.feeBaseMsatNode1_opt = Some(channelUpdate.feeBaseMsat) + c.feeBaseMsatNode1_opt = Some(channelUpdate.feeBaseMsat.toLong) c.feeProportionalMillionthsNode1_opt = Some(channelUpdate.feeProportionalMillionths) } else { c.isNode2Enabled = Some(Announcements.isEnabled(channelUpdate.channelFlags)) - c.feeBaseMsatNode2_opt = Some(channelUpdate.feeBaseMsat) + c.feeBaseMsatNode2_opt = Some(channelUpdate.feeBaseMsat.toLong) c.feeProportionalMillionthsNode2_opt = Some(channelUpdate.feeProportionalMillionths) } mainController.networkChannelsMap.put(channelUpdate.shortChannelId, c) @@ -200,7 +200,7 @@ class GUIUpdater(mainController: MainController) extends Actor with ActorLogging } case p: PaymentSucceeded => - val message = CoinUtils.formatAmountInUnit(MilliSatoshi(p.amountMsat), FxApp.getUnit, withUnit = true) + val message = CoinUtils.formatAmountInUnit(p.amount, FxApp.getUnit, withUnit = true) mainController.handlers.notification("Payment Sent", message, NOTIFICATION_SUCCESS) case p: PaymentFailed => diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/Handlers.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/Handlers.scala index 80e286051b..54765f834a 100644 --- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/Handlers.scala +++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/Handlers.scala @@ -20,7 +20,7 @@ import java.util.UUID import akka.pattern.{AskTimeoutException, ask} import akka.util.Timeout -import fr.acinq.bitcoin.MilliSatoshi +import fr.acinq.eclair.MilliSatoshi import fr.acinq.eclair._ import fr.acinq.eclair.gui.controllers._ import fr.acinq.eclair.io.{NodeURI, Peer} @@ -88,8 +88,8 @@ class Handlers(fKit: Future[Kit])(implicit ec: ExecutionContext = ExecutionConte (for { kit <- fKit sendPayment = req.minFinalCltvExpiry match { - case None => SendPayment(amountMsat, req.paymentHash, req.nodeId, req.routingInfo, maxAttempts = kit.nodeParams.maxPaymentAttempts) - case Some(minFinalCltvExpiry) => SendPayment(amountMsat, req.paymentHash, req.nodeId, req.routingInfo, finalCltvExpiry = minFinalCltvExpiry, maxAttempts = kit.nodeParams.maxPaymentAttempts) + case None => SendPayment(MilliSatoshi(amountMsat), req.paymentHash, req.nodeId, req.routingInfo, maxAttempts = kit.nodeParams.maxPaymentAttempts) + case Some(minFinalCltvExpiry) => SendPayment(MilliSatoshi(amountMsat), req.paymentHash, req.nodeId, req.routingInfo, finalCltvExpiry = minFinalCltvExpiry, maxAttempts = kit.nodeParams.maxPaymentAttempts) } res <- (kit.paymentInitiator ? sendPayment).mapTo[UUID] } yield res).recover { diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/ChannelPaneController.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/ChannelPaneController.scala index 13e9be2d83..c4691f0148 100644 --- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/ChannelPaneController.scala +++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/ChannelPaneController.scala @@ -18,7 +18,7 @@ package fr.acinq.eclair.gui.controllers import akka.actor.ActorRef import com.google.common.base.Strings -import fr.acinq.bitcoin.MilliSatoshi +import fr.acinq.eclair.MilliSatoshi import fr.acinq.eclair.CoinUtils import fr.acinq.eclair.channel.{CMD_CLOSE, CMD_FORCECLOSE, Commitments} import fr.acinq.eclair.gui.FxApp @@ -129,12 +129,12 @@ class ChannelPaneController(val channelRef: ActorRef, val peerNodeId: String) ex } def updateBalance(commitments: Commitments) { - balance = MilliSatoshi(commitments.localCommit.spec.toLocalMsat) - capacity = MilliSatoshi(commitments.localCommit.spec.totalFunds) + balance = commitments.localCommit.spec.toLocal + capacity = commitments.localCommit.spec.totalFunds } def refreshBalance(): Unit = { - amountUs.setText(s"${CoinUtils.formatAmountInUnit(balance, FxApp.getUnit)} / ${CoinUtils.formatAmountInUnit(capacity, FxApp.getUnit, withUnit = true)}") + amountUs.setText(s"${CoinUtils.formatAmountInUnit(balance, FxApp.getUnit, false)} / ${CoinUtils.formatAmountInUnit(capacity, FxApp.getUnit, withUnit = true)}") balanceBar.setProgress(balance.amount.toDouble / capacity.amount) } diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/MainController.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/MainController.scala index 1a8707be1c..60626148c8 100644 --- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/MainController.scala +++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/MainController.scala @@ -23,14 +23,14 @@ import java.util.Locale import com.google.common.net.HostAndPort import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{MilliSatoshi, Satoshi} +import fr.acinq.bitcoin.Satoshi import fr.acinq.eclair.NodeParams.{BITCOIND, ELECTRUM} import fr.acinq.eclair.gui.stages._ import fr.acinq.eclair.gui.utils.{ContextMenuUtils, CopyAction, IndexedObservableList} import fr.acinq.eclair.gui.{FxApp, Handlers} import fr.acinq.eclair.payment.{PaymentEvent, PaymentReceived, PaymentRelayed, PaymentSent} import fr.acinq.eclair.wire.{ChannelAnnouncement, NodeAnnouncement} -import fr.acinq.eclair.{CoinUtils, Setup, ShortChannelId} +import fr.acinq.eclair.{CoinUtils, MilliSatoshi, Setup, ShortChannelId} import grizzled.slf4j.Logging import javafx.animation.{FadeTransition, ParallelTransition, SequentialTransition, TranslateTransition} import javafx.application.{HostServices, Platform} diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/OpenChannelController.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/OpenChannelController.scala index 9f6560553e..361ba9337a 100644 --- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/OpenChannelController.scala +++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/OpenChannelController.scala @@ -19,12 +19,13 @@ package fr.acinq.eclair.gui.controllers import java.lang.Boolean import com.google.common.base.Strings -import fr.acinq.bitcoin.{Satoshi, _} +import fr.acinq.bitcoin.Satoshi +import fr.acinq.eclair._ 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} +import fr.acinq.eclair.{CoinUtils, Globals, MilliSatoshi} import grizzled.slf4j.Logging import javafx.beans.value.{ChangeListener, ObservableValue} import javafx.event.ActionEvent @@ -79,8 +80,8 @@ class OpenChannelController(val handlers: Handlers, val stage: Stage) extends Lo fundingSatError.setText("Capacity must be greater than 0") case Success(capacitySat) if capacitySat.amount < 50000 => fundingSatError.setText("Capacity is low and the channel may not be able to open") - case Success(capacitySat) if capacitySat.amount >= Channel.MAX_FUNDING_SATOSHIS => - fundingSatError.setText(s"Capacity must be less than ${CoinUtils.formatAmountInUnit(Satoshi(Channel.MAX_FUNDING_SATOSHIS), FxApp.getUnit, withUnit = true)}") + case Success(capacitySat) if capacitySat >= Channel.MAX_FUNDING => + fundingSatError.setText(s"Capacity must be less than ${CoinUtils.formatAmountInUnit(Channel.MAX_FUNDING, FxApp.getUnit, withUnit = true)}") case Success(_) => fundingSatError.setText("") case _ => fundingSatError.setText("Capacity is not valid") } @@ -100,9 +101,9 @@ class OpenChannelController(val handlers: Handlers, val stage: Stage) extends Lo Try(if (Strings.isNullOrEmpty(feerateField.getText())) None else Some(feerateField.getText().toLong))) match { case (Success(capacitySat), _, _) if capacitySat.amount <= 0 => fundingSatError.setText("Capacity must be greater than 0") - case (Success(capacitySat), _, _) if capacitySat.amount >= Channel.MAX_FUNDING_SATOSHIS => - fundingSatError.setText(s"Capacity must be less than ${CoinUtils.formatAmountInUnit(Satoshi(Channel.MAX_FUNDING_SATOSHIS), FxApp.getUnit, withUnit = true)}") - case (Success(capacitySat), Success(pushMsat), _) if pushMsat > satoshi2millisatoshi(capacitySat).amount => + case (Success(capacitySat), _, _) if capacitySat >= Channel.MAX_FUNDING => + fundingSatError.setText(s"Capacity must be less than ${CoinUtils.formatAmountInUnit(Channel.MAX_FUNDING, FxApp.getUnit, withUnit = true)}") + case (Success(capacitySat), Success(pushMsat), _) if pushMsat > capacitySat.toMilliSatoshi.toLong => pushMsatError.setText("Push must be less or equal to capacity") case (Success(_), Success(pushMsat), _) if pushMsat < 0 => pushMsatError.setText("Push must be positive") diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/ReceivePaymentController.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/ReceivePaymentController.scala index b0ad2fe916..2abe281afb 100644 --- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/ReceivePaymentController.scala +++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/ReceivePaymentController.scala @@ -24,7 +24,7 @@ import javafx.scene.image.{ImageView, WritableImage} import javafx.scene.layout.GridPane import javafx.stage.Stage -import fr.acinq.bitcoin.MilliSatoshi +import fr.acinq.eclair.MilliSatoshi import fr.acinq.eclair.CoinUtils import fr.acinq.eclair.gui.{FxApp, Handlers} import fr.acinq.eclair.gui.utils._ diff --git a/pom.xml b/pom.xml index ed1e45b218..e5aa8bbc1d 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ 2.4.20 10.0.11 1.3.9 - 0.13 + 0.14 24.0-android