From b73c0ec710b2796188e14150e5335bbfce39b27c Mon Sep 17 00:00:00 2001 From: Thomas HUET Date: Tue, 1 Mar 2022 12:27:41 +0100 Subject: [PATCH 1/3] Use minFinalExpiryDelta from spec An invoice that doesn't explicitly set min_final_cltv_expiry has an default min_final_cltv_expiry of 18. This default allows invoices to be slightly shorter but does not mean that the min_final_cltv_expiry can be unknown and needs to be provided from somewhere else. --- .../main/scala/fr/acinq/eclair/Eclair.scala | 11 +++--- .../fr/acinq/eclair/channel/Channel.scala | 4 --- .../fr/acinq/eclair/channel/Commitments.scala | 4 +-- .../acinq/eclair/json/JsonSerializers.scala | 2 +- .../acinq/eclair/payment/Bolt11Invoice.scala | 6 +++- .../fr/acinq/eclair/payment/Invoice.scala | 2 +- .../payment/receive/MultiPartHandler.scala | 2 +- .../payment/send/PaymentInitiator.scala | 11 ++---- .../fr/acinq/eclair/EclairImplSpec.scala | 5 ++- .../fr/acinq/eclair/channel/FuzzySpec.scala | 2 +- .../channel/states/e/NormalStateSpec.scala | 6 ++-- .../integration/ChannelIntegrationSpec.scala | 4 +-- .../integration/PaymentIntegrationSpec.scala | 16 ++++----- .../PerformanceIntegrationSpec.scala | 2 +- .../eclair/payment/Bolt11InvoiceSpec.scala | 6 ++-- .../eclair/payment/MultiPartHandlerSpec.scala | 4 +-- .../eclair/payment/PaymentInitiatorSpec.scala | 34 +++++++++---------- .../eclair/payment/PaymentLifecycleSpec.scala | 4 +-- .../eclair/payment/PaymentPacketSpec.scala | 2 +- .../acinq/eclair/api/handlers/Payment.scala | 6 ++-- .../fr/acinq/eclair/api/ApiServiceSpec.scala | 8 ++--- 21 files changed, 64 insertions(+), 77 deletions(-) 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 6f98a0a5ec..afdd80e517 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala @@ -125,7 +125,7 @@ trait Eclair { def findRouteBetween(sourceNodeId: PublicKey, targetNodeId: PublicKey, amount: MilliSatoshi, pathFindingExperimentName_opt: Option[String], assistedRoutes: Seq[Seq[Bolt11Invoice.ExtraHop]] = Seq.empty, includeLocalChannelCost: Boolean = false, ignoreNodeIds: Seq[PublicKey] = Seq.empty, ignoreShortChannelIds: Seq[ShortChannelId] = Seq.empty, maxFee_opt: Option[MilliSatoshi] = None)(implicit timeout: Timeout): Future[RouteResponse] - def sendToRoute(amount: MilliSatoshi, recipientAmount_opt: Option[MilliSatoshi], externalId_opt: Option[String], parentId_opt: Option[UUID], invoice: Bolt11Invoice, finalCltvExpiryDelta: CltvExpiryDelta, route: PredefinedRoute, trampolineSecret_opt: Option[ByteVector32] = None, trampolineFees_opt: Option[MilliSatoshi] = None, trampolineExpiryDelta_opt: Option[CltvExpiryDelta] = None, trampolineNodes_opt: Seq[PublicKey] = Nil)(implicit timeout: Timeout): Future[SendPaymentToRouteResponse] + def sendToRoute(amount: MilliSatoshi, recipientAmount_opt: Option[MilliSatoshi], externalId_opt: Option[String], parentId_opt: Option[UUID], invoice: Bolt11Invoice, route: PredefinedRoute, trampolineSecret_opt: Option[ByteVector32] = None, trampolineFees_opt: Option[MilliSatoshi] = None, trampolineExpiryDelta_opt: Option[CltvExpiryDelta] = None, trampolineNodes_opt: Seq[PublicKey] = Nil)(implicit timeout: Timeout): Future[SendPaymentToRouteResponse] def audit(from: TimestampSecond, to: TimestampSecond)(implicit timeout: Timeout): Future[AuditResponse] @@ -309,9 +309,9 @@ class EclairImpl(appKit: Kit) extends Eclair with Logging { } } - override def sendToRoute(amount: MilliSatoshi, recipientAmount_opt: Option[MilliSatoshi], externalId_opt: Option[String], parentId_opt: Option[UUID], invoice: Bolt11Invoice, finalCltvExpiryDelta: CltvExpiryDelta, route: PredefinedRoute, trampolineSecret_opt: Option[ByteVector32], trampolineFees_opt: Option[MilliSatoshi], trampolineExpiryDelta_opt: Option[CltvExpiryDelta], trampolineNodes_opt: Seq[PublicKey])(implicit timeout: Timeout): Future[SendPaymentToRouteResponse] = { + override def sendToRoute(amount: MilliSatoshi, recipientAmount_opt: Option[MilliSatoshi], externalId_opt: Option[String], parentId_opt: Option[UUID], invoice: Bolt11Invoice, route: PredefinedRoute, trampolineSecret_opt: Option[ByteVector32], trampolineFees_opt: Option[MilliSatoshi], trampolineExpiryDelta_opt: Option[CltvExpiryDelta], trampolineNodes_opt: Seq[PublicKey])(implicit timeout: Timeout): Future[SendPaymentToRouteResponse] = { val recipientAmount = recipientAmount_opt.getOrElse(invoice.amount_opt.getOrElse(amount)) - val sendPayment = SendPaymentToRoute(amount, recipientAmount, invoice, finalCltvExpiryDelta, route, externalId_opt, parentId_opt, trampolineSecret_opt, trampolineFees_opt.getOrElse(0 msat), trampolineExpiryDelta_opt.getOrElse(CltvExpiryDelta(0)), trampolineNodes_opt) + val sendPayment = SendPaymentToRoute(amount, recipientAmount, invoice, route, externalId_opt, parentId_opt, trampolineSecret_opt, trampolineFees_opt.getOrElse(0 msat), trampolineExpiryDelta_opt.getOrElse(CltvExpiryDelta(0)), trampolineNodes_opt) if (invoice.isExpired()) { Future.failed(new IllegalArgumentException("invoice has expired")) } else if (route.isEmpty) { @@ -337,10 +337,7 @@ class EclairImpl(appKit: Kit) extends Eclair with Logging { externalId_opt match { case Some(externalId) if externalId.length > externalIdMaxLength => Left(new IllegalArgumentException(s"externalId is too long: cannot exceed $externalIdMaxLength characters")) case _ if invoice.isExpired() => Left(new IllegalArgumentException("invoice has expired")) - case _ => invoice.minFinalCltvExpiryDelta match { - case Some(minFinalCltvExpiryDelta) => Right(SendPaymentToNode(amount, invoice, maxAttempts, minFinalCltvExpiryDelta, externalId_opt, assistedRoutes = invoice.routingInfo, routeParams = routeParams)) - case None => Right(SendPaymentToNode(amount, invoice, maxAttempts, externalId = externalId_opt, assistedRoutes = invoice.routingInfo, routeParams = routeParams)) - } + case _ => Right(SendPaymentToNode(amount, invoice, maxAttempts, externalId_opt, assistedRoutes = invoice.routingInfo, routeParams = routeParams)) } case Left(t) => Left(t) } 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 11ca879dd9..3076d5db9f 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 @@ -114,10 +114,6 @@ object Channel { // we won't exchange more than this many signatures when negotiating the closing fee val MAX_NEGOTIATION_ITERATIONS = 20 - // this is defined in BOLT 11 - val MIN_CLTV_EXPIRY_DELTA: CltvExpiryDelta = CltvExpiryDelta(18) - val MAX_CLTV_EXPIRY_DELTA: CltvExpiryDelta = CltvExpiryDelta(7 * 144) // one week - // since BOLT 1.1, there is a max value for the refund delay of the main commitment tx val MAX_TO_SELF_DELAY: CltvExpiryDelta = CltvExpiryDelta(2016) 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 5b483b21b5..295f0ad55b 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala @@ -25,7 +25,7 @@ import fr.acinq.eclair.channel.Helpers.Closing import fr.acinq.eclair.channel.Monitoring.Metrics import fr.acinq.eclair.crypto.keymanager.ChannelKeyManager import fr.acinq.eclair.crypto.{Generators, ShaChain} -import fr.acinq.eclair.payment.OutgoingPaymentPacket +import fr.acinq.eclair.payment.{Bolt11Invoice, OutgoingPaymentPacket} import fr.acinq.eclair.transactions.DirectedHtlc._ import fr.acinq.eclair.transactions.Transactions._ import fr.acinq.eclair.transactions._ @@ -324,7 +324,7 @@ object Commitments { return Left(ExpiryTooSmall(commitments.channelId, minimum = minExpiry, actual = cmd.cltvExpiry, blockHeight = currentHeight)) } // we don't want to use too high a refund timeout, because our funds will be locked during that time if the payment is never fulfilled - val maxExpiry = Channel.MAX_CLTV_EXPIRY_DELTA.toCltvExpiry(currentHeight) + val maxExpiry = Bolt11Invoice.MAX_CLTV_EXPIRY_DELTA.toCltvExpiry(currentHeight) if (cmd.cltvExpiry >= maxExpiry) { return Left(ExpiryTooBig(commitments.channelId, maximum = maxExpiry, actual = cmd.cltvExpiry, blockHeight = currentHeight)) } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/json/JsonSerializers.scala b/eclair-core/src/main/scala/fr/acinq/eclair/json/JsonSerializers.scala index 4c3479bd2e..85e6605d86 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/json/JsonSerializers.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/json/JsonSerializers.scala @@ -342,7 +342,7 @@ object DirectedHtlcSerializer extends ConvertClassSerializer[DirectedHtlc](h => object InvoiceSerializer extends MinimalSerializer({ case p: Bolt11Invoice => val expiry = p.relativeExpiry_opt.map(ex => JField("expiry", JLong(ex))).toSeq - val minFinalCltvExpiry = p.minFinalCltvExpiryDelta.map(mfce => JField("minFinalCltvExpiry", JInt(mfce.toInt))).toSeq + val minFinalCltvExpiry = p.minFinalCltvExpiryDelta_opt.map(mfce => JField("minFinalCltvExpiry", JInt(mfce.toInt))).toSeq val amount = p.amount_opt.map(msat => JField("amount", JLong(msat.toLong))).toSeq val features = JField("features", Extraction.decompose(p.features)( DefaultFormats + diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Bolt11Invoice.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Bolt11Invoice.scala index cfebfbf069..76d22909fb 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Bolt11Invoice.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Bolt11Invoice.scala @@ -89,10 +89,12 @@ case class Bolt11Invoice(prefix: String, amount_opt: Option[MilliSatoshi], creat lazy val relativeExpiry: FiniteDuration = FiniteDuration(relativeExpiry_opt.getOrElse(DEFAULT_EXPIRY_SECONDS), TimeUnit.SECONDS) - lazy val minFinalCltvExpiryDelta: Option[CltvExpiryDelta] = tags.collectFirst { + lazy val minFinalCltvExpiryDelta_opt: Option[CltvExpiryDelta] = tags.collectFirst { case cltvExpiry: Bolt11Invoice.MinFinalCltvExpiry => cltvExpiry.toCltvExpiryDelta } + lazy val minFinalCltvExpiryDelta: CltvExpiryDelta = minFinalCltvExpiryDelta_opt.getOrElse(MIN_CLTV_EXPIRY_DELTA) + lazy val features: Features[InvoiceFeature] = tags.collectFirst { case f: InvoiceFeatures => f.features.invoiceFeatures() }.getOrElse(Features.empty[InvoiceFeature]) /** @@ -134,6 +136,8 @@ case class Bolt11Invoice(prefix: String, amount_opt: Option[MilliSatoshi], creat object Bolt11Invoice { val DEFAULT_EXPIRY_SECONDS: Long = 3600 + val MIN_CLTV_EXPIRY_DELTA: CltvExpiryDelta = CltvExpiryDelta(18) + val MAX_CLTV_EXPIRY_DELTA: CltvExpiryDelta = CltvExpiryDelta(7 * 144) // one week val prefixes = Map( Block.RegtestGenesisBlock.hash -> "lnbcrt", diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Invoice.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Invoice.scala index c721b57d7c..bd2ebdfbe5 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Invoice.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Invoice.scala @@ -42,7 +42,7 @@ trait Invoice { val relativeExpiry: FiniteDuration - val minFinalCltvExpiryDelta: Option[CltvExpiryDelta] + val minFinalCltvExpiryDelta: CltvExpiryDelta val features: Features[InvoiceFeature] diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/receive/MultiPartHandler.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/receive/MultiPartHandler.scala index 8aa23167f4..9d699ed335 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/receive/MultiPartHandler.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/receive/MultiPartHandler.scala @@ -306,7 +306,7 @@ object MultiPartHandler { } private def validatePaymentCltv(nodeParams: NodeParams, payment: IncomingPaymentPacket.FinalPacket, record: IncomingPayment)(implicit log: LoggingAdapter): Boolean = { - val minExpiry = record.invoice.minFinalCltvExpiryDelta.getOrElse(nodeParams.channelConf.minFinalExpiryDelta).toCltvExpiry(nodeParams.currentBlockHeight) + val minExpiry = record.invoice.minFinalCltvExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight) if (payment.add.cltvExpiry < minExpiry) { log.warning("received payment with expiry too small for amount={} totalAmount={}", payment.add.amountMsat, payment.payload.totalAmount) false diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/send/PaymentInitiator.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/send/PaymentInitiator.scala index b5d273d9c0..7b0deb2a38 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/send/PaymentInitiator.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/send/PaymentInitiator.scala @@ -73,7 +73,7 @@ class PaymentInitiator(nodeParams: NodeParams, outgoingPaymentFactory: PaymentIn val paymentId = UUID.randomUUID() sender() ! paymentId val paymentCfg = SendPaymentConfig(paymentId, paymentId, r.externalId, r.paymentHash, r.recipientAmount, r.recipientNodeId, Upstream.Local(paymentId), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = r.recordPathFindingMetrics, Nil) - val finalExpiry = Channel.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry(nodeParams.currentBlockHeight + 1) + val finalExpiry = Bolt11Invoice.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry(nodeParams.currentBlockHeight + 1) val finalPayload = PaymentOnion.FinalTlvPayload(TlvStream(Seq(OnionPaymentPayloadTlv.AmountToForward(r.recipientAmount), OnionPaymentPayloadTlv.OutgoingCltv(finalExpiry), OnionPaymentPayloadTlv.PaymentData(randomBytes32(), r.recipientAmount), OnionPaymentPayloadTlv.KeySend(r.paymentPreimage)), r.userCustomTlvs)) val fsm = outgoingPaymentFactory.spawnOutgoingPayment(context, paymentCfg) fsm ! PaymentLifecycle.SendPaymentToNode(self, r.recipientNodeId, finalPayload, r.maxAttempts, routeParams = r.routeParams) @@ -272,9 +272,8 @@ object PaymentInitiator { def invoice: Invoice def recipientNodeId: PublicKey = invoice.nodeId def paymentHash: ByteVector32 = invoice.paymentHash - def fallbackFinalExpiryDelta: CltvExpiryDelta // We add one block in order to not have our htlcs fail when a new block has just been found. - def finalExpiry(currentBlockHeight: BlockHeight): CltvExpiry = invoice.minFinalCltvExpiryDelta.getOrElse(fallbackFinalExpiryDelta).toCltvExpiry(currentBlockHeight + 1) + def finalExpiry(currentBlockHeight: BlockHeight): CltvExpiry = invoice.minFinalCltvExpiryDelta.toCltvExpiry(currentBlockHeight + 1) // @formatter:on } @@ -290,21 +289,18 @@ object PaymentInitiator { * the payment will automatically be retried in case of TrampolineFeeInsufficient errors. * For example, [(10 msat, 144), (15 msat, 288)] will first send a payment with a fee of 10 * msat and cltv of 144, and retry with 15 msat and 288 in case an error occurs. - * @param fallbackFinalExpiryDelta expiry delta for the final recipient when the [[invoice]] doesn't specify it. * @param routeParams (optional) parameters to fine-tune the routing algorithm. */ case class SendTrampolinePayment(recipientAmount: MilliSatoshi, invoice: Invoice, trampolineNodeId: PublicKey, trampolineAttempts: Seq[(MilliSatoshi, CltvExpiryDelta)], - fallbackFinalExpiryDelta: CltvExpiryDelta = Channel.MIN_CLTV_EXPIRY_DELTA, routeParams: RouteParams) extends SendRequestedPayment /** * @param recipientAmount amount that should be received by the final recipient (usually from a Bolt 11 invoice). * @param invoice Bolt 11 invoice. * @param maxAttempts maximum number of retries. - * @param fallbackFinalExpiryDelta expiry delta for the final recipient when the [[invoice]] doesn't specify it. * @param externalId (optional) externally-controlled identifier (to reconcile between application DB and eclair DB). * @param assistedRoutes (optional) routing hints (usually from a Bolt 11 invoice). * @param routeParams (optional) parameters to fine-tune the routing algorithm. @@ -314,7 +310,6 @@ object PaymentInitiator { case class SendPaymentToNode(recipientAmount: MilliSatoshi, invoice: Invoice, maxAttempts: Int, - fallbackFinalExpiryDelta: CltvExpiryDelta = Channel.MIN_CLTV_EXPIRY_DELTA, externalId: Option[String] = None, assistedRoutes: Seq[Seq[ExtraHop]] = Nil, routeParams: RouteParams, @@ -360,7 +355,6 @@ object PaymentInitiator { * @param recipientAmount amount that should be received by the final recipient (usually from a Bolt 11 invoice). * This amount may be split between multiple requests if using MPP. * @param invoice Bolt 11 invoice. - * @param fallbackFinalExpiryDelta expiry delta for the final recipient when the [[invoice]] doesn't specify it. * @param route route to use to reach either the final recipient or the first trampoline node. * @param externalId (optional) externally-controlled identifier (to reconcile between application DB and eclair DB). * @param parentId id of the whole payment. When manually sending a multi-part payment, you need to make @@ -379,7 +373,6 @@ object PaymentInitiator { case class SendPaymentToRoute(amount: MilliSatoshi, recipientAmount: MilliSatoshi, invoice: Invoice, - fallbackFinalExpiryDelta: CltvExpiryDelta = Channel.MIN_CLTV_EXPIRY_DELTA, route: PredefinedRoute, externalId: Option[String], parentId: Option[UUID], 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 4384214a94..bdd2efe22b 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala @@ -145,7 +145,6 @@ class EclairImplSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with I assert(send2.recipientAmount === 123.msat) assert(send2.paymentHash === ByteVector32.Zeroes) assert(send2.invoice === invoice2) - assert(send2.fallbackFinalExpiryDelta === CltvExpiryDelta(96)) // with custom route fees parameters eclair.send(None, 123 msat, invoice0, maxFeeFlat_opt = Some(123 sat), maxFeePct_opt = Some(4.20)) @@ -309,8 +308,8 @@ class EclairImplSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with I val parentId = UUID.randomUUID() val secret = randomBytes32() val pr = Bolt11Invoice(Block.LivenetGenesisBlock.hash, Some(1234 msat), ByteVector32.One, randomKey(), Right(randomBytes32()), CltvExpiryDelta(18)) - eclair.sendToRoute(1000 msat, Some(1200 msat), Some("42"), Some(parentId), pr, CltvExpiryDelta(123), route, Some(secret), Some(100 msat), Some(CltvExpiryDelta(144)), trampolines) - paymentInitiator.expectMsg(SendPaymentToRoute(1000 msat, 1200 msat, pr, CltvExpiryDelta(123), route, Some("42"), Some(parentId), Some(secret), 100 msat, CltvExpiryDelta(144), trampolines)) + eclair.sendToRoute(1000 msat, Some(1200 msat), Some("42"), Some(parentId), pr, route, Some(secret), Some(100 msat), Some(CltvExpiryDelta(144)), trampolines) + paymentInitiator.expectMsg(SendPaymentToRoute(1000 msat, 1200 msat, pr, route, Some("42"), Some(parentId), Some(secret), 100 msat, CltvExpiryDelta(144), trampolines)) } test("call sendWithPreimage, which generates a random preimage, to perform a KeySend payment") { f => 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 a968c65f25..0885c89e10 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 @@ -121,7 +121,7 @@ class FuzzySpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with Channe def buildCmdAdd(paymentHash: ByteVector32, dest: PublicKey, paymentSecret: ByteVector32): CMD_ADD_HTLC = { // allow overpaying (no more than 2 times the required amount) val amount = requiredAmount + Random.nextInt(requiredAmount.toLong.toInt).msat - val expiry = (Channel.MIN_CLTV_EXPIRY_DELTA + 1).toCltvExpiry(currentBlockHeight = BlockHeight(400000)) + val expiry = (Bolt11Invoice.MIN_CLTV_EXPIRY_DELTA + 1).toCltvExpiry(currentBlockHeight = BlockHeight(400000)) OutgoingPaymentPacket.buildCommand(self, Upstream.Local(UUID.randomUUID()), paymentHash, ChannelHop(null, dest, null) :: Nil, PaymentOnion.createSinglePartPayload(amount, expiry, paymentSecret, None)).get._1 } 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 c63d9ade71..277debe04b 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 @@ -33,7 +33,7 @@ import fr.acinq.eclair.channel.publish.TxPublisher.{PublishFinalTx, PublishRepla import fr.acinq.eclair.channel.states.{ChannelStateTestsBase, ChannelStateTestsTags} import fr.acinq.eclair.crypto.Sphinx import fr.acinq.eclair.io.Peer -import fr.acinq.eclair.payment.OutgoingPaymentPacket +import fr.acinq.eclair.payment.{Bolt11Invoice, OutgoingPaymentPacket} import fr.acinq.eclair.payment.relay.Relayer._ import fr.acinq.eclair.router.Announcements import fr.acinq.eclair.transactions.DirectedHtlc.{incoming, outgoing} @@ -158,10 +158,10 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with import f._ val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - val expiryTooBig = (Channel.MAX_CLTV_EXPIRY_DELTA + 1).toCltvExpiry(currentBlockHeight) + val expiryTooBig = (Bolt11Invoice.MAX_CLTV_EXPIRY_DELTA + 1).toCltvExpiry(currentBlockHeight) val add = CMD_ADD_HTLC(sender.ref, 500000000 msat, randomBytes32(), expiryTooBig, TestConstants.emptyOnionPacket, localOrigin(sender.ref)) alice ! add - val error = ExpiryTooBig(channelId(alice), maximum = Channel.MAX_CLTV_EXPIRY_DELTA.toCltvExpiry(currentBlockHeight), actual = expiryTooBig, blockHeight = currentBlockHeight) + val error = ExpiryTooBig(channelId(alice), maximum = Bolt11Invoice.MAX_CLTV_EXPIRY_DELTA.toCltvExpiry(currentBlockHeight), actual = expiryTooBig, blockHeight = currentBlockHeight) sender.expectMsg(RES_ADD_FAILED(add, error, Some(initialState.channelUpdate))) alice2bob.expectNoMessage(200 millis) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/integration/ChannelIntegrationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/integration/ChannelIntegrationSpec.scala index ae970d342f..22c50677e5 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/integration/ChannelIntegrationSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/integration/ChannelIntegrationSpec.scala @@ -368,7 +368,7 @@ abstract class ChannelIntegrationSpec extends IntegrationSpec { def send(amountMsat: MilliSatoshi, paymentHandler: ActorRef, paymentInitiator: ActorRef): UUID = { sender.send(paymentHandler, ReceivePayment(Some(amountMsat), Left("1 coffee"))) val invoice = sender.expectMsgType[Invoice] - val sendReq = SendPaymentToNode(amountMsat, invoice, maxAttempts = 1, fallbackFinalExpiryDelta = finalCltvExpiryDelta, routeParams = integrationTestRouteParams) + val sendReq = SendPaymentToNode(amountMsat, invoice, maxAttempts = 1, routeParams = integrationTestRouteParams) sender.send(paymentInitiator, sendReq) sender.expectMsgType[UUID] } @@ -684,7 +684,7 @@ abstract class AnchorChannelIntegrationSpec extends ChannelIntegrationSpec { val invoice = sender.expectMsgType[Invoice] // then we make the actual payment - sender.send(nodes("C").paymentInitiator, SendPaymentToNode(amountMsat, invoice, maxAttempts = 1, fallbackFinalExpiryDelta = finalCltvExpiryDelta, routeParams = integrationTestRouteParams)) + sender.send(nodes("C").paymentInitiator, SendPaymentToNode(amountMsat, invoice, maxAttempts = 1, routeParams = integrationTestRouteParams)) val paymentId = sender.expectMsgType[UUID] val ps = sender.expectMsgType[PaymentSent](60 seconds) assert(ps.id == paymentId) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/integration/PaymentIntegrationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/integration/PaymentIntegrationSpec.scala index 4dff2e291b..c351c4153e 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/integration/PaymentIntegrationSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/integration/PaymentIntegrationSpec.scala @@ -163,7 +163,7 @@ class PaymentIntegrationSpec extends IntegrationSpec { assert(invoice.paymentMetadata.nonEmpty) // then we make the actual payment - sender.send(nodes("A").paymentInitiator, SendPaymentToNode(amountMsat, invoice, fallbackFinalExpiryDelta = finalCltvExpiryDelta, routeParams = integrationTestRouteParams, maxAttempts = 1)) + sender.send(nodes("A").paymentInitiator, SendPaymentToNode(amountMsat, invoice, routeParams = integrationTestRouteParams, maxAttempts = 1)) val paymentId = sender.expectMsgType[UUID] val ps = sender.expectMsgType[PaymentSent] assert(ps.id == paymentId) @@ -189,7 +189,7 @@ class PaymentIntegrationSpec extends IntegrationSpec { sender.send(nodes("D").paymentHandler, ReceivePayment(Some(amountMsat), Left("1 coffee"))) val invoice = sender.expectMsgType[Invoice] // then we make the actual payment, do not randomize the route to make sure we route through node B - val sendReq = SendPaymentToNode(amountMsat, invoice, fallbackFinalExpiryDelta = finalCltvExpiryDelta, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPaymentToNode(amountMsat, invoice, 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] @@ -230,7 +230,7 @@ class PaymentIntegrationSpec extends IntegrationSpec { sender.send(nodes("D").paymentHandler, ReceivePayment(Some(amountMsat), Left("1 coffee"))) val invoice = sender.expectMsgType[Invoice] // then we make the payment (B-C has a smaller capacity than A-B and C-D) - val sendReq = SendPaymentToNode(amountMsat, invoice, fallbackFinalExpiryDelta = finalCltvExpiryDelta, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPaymentToNode(amountMsat, invoice, 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] @@ -261,7 +261,7 @@ class PaymentIntegrationSpec extends IntegrationSpec { val invoice = sender.expectMsgType[Invoice] // A send payment of only 1 mBTC - val sendReq = SendPaymentToNode(100000000 msat, invoice, fallbackFinalExpiryDelta = finalCltvExpiryDelta, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPaymentToNode(100000000 msat, invoice, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, sendReq) // A will first receive an IncorrectPaymentAmount error from D @@ -281,7 +281,7 @@ class PaymentIntegrationSpec extends IntegrationSpec { val invoice = sender.expectMsgType[Invoice] // A send payment of 6 mBTC - val sendReq = SendPaymentToNode(600000000 msat, invoice, fallbackFinalExpiryDelta = finalCltvExpiryDelta, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPaymentToNode(600000000 msat, invoice, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, sendReq) // A will first receive an IncorrectPaymentAmount error from D @@ -301,7 +301,7 @@ class PaymentIntegrationSpec extends IntegrationSpec { val invoice = sender.expectMsgType[Invoice] // A send payment of 3 mBTC, more than asked but it should still be accepted - val sendReq = SendPaymentToNode(300000000 msat, invoice, fallbackFinalExpiryDelta = finalCltvExpiryDelta, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPaymentToNode(300000000 msat, invoice, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, sendReq) sender.expectMsgType[UUID] } @@ -314,7 +314,7 @@ class PaymentIntegrationSpec extends IntegrationSpec { sender.send(nodes("D").paymentHandler, ReceivePayment(Some(amountMsat), Left("1 payment"))) val invoice = sender.expectMsgType[Invoice] - val sendReq = SendPaymentToNode(amountMsat, invoice, fallbackFinalExpiryDelta = finalCltvExpiryDelta, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPaymentToNode(amountMsat, invoice, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, sendReq) sender.expectMsgType[UUID] sender.expectMsgType[PaymentSent] // the payment FSM will also reply to the sender after the payment is completed @@ -329,7 +329,7 @@ class PaymentIntegrationSpec extends IntegrationSpec { val invoice = sender.expectMsgType[Invoice] // 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, SendPaymentToNode(amountMsat, invoice, maxAttempts = 1, fallbackFinalExpiryDelta = finalCltvExpiryDelta, routeParams = integrationTestRouteParams.copy(heuristics = Left(WeightRatios(0, 0, 0, 1, RelayFees(0 msat, 0)))))) + sender.send(nodes("A").paymentInitiator, SendPaymentToNode(amountMsat, invoice, maxAttempts = 1, routeParams = integrationTestRouteParams.copy(heuristics = Left(WeightRatios(0, 0, 0, 1, RelayFees(0 msat, 0)))))) sender.expectMsgType[UUID] val ps = sender.expectMsgType[PaymentSent] ps.parts.foreach(part => assert(part.route.getOrElse(Nil).exists(_.nodeId == nodes("G").nodeParams.nodeId))) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/integration/PerformanceIntegrationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/integration/PerformanceIntegrationSpec.scala index a1cdfe2be8..516743a068 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/integration/PerformanceIntegrationSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/integration/PerformanceIntegrationSpec.scala @@ -86,7 +86,7 @@ class PerformanceIntegrationSpec extends IntegrationSpec { sender.send(nodes("B").paymentHandler, ReceivePayment(Some(amountMsat), Left("1 coffee"))) val pr = sender.expectMsgType[Invoice] // then we make the actual payment - sender.send(nodes("A").paymentInitiator, PaymentInitiator.SendPaymentToNode(amountMsat, pr, fallbackFinalExpiryDelta = finalCltvExpiryDelta, routeParams = integrationTestRouteParams, maxAttempts = 1)) + sender.send(nodes("A").paymentInitiator, PaymentInitiator.SendPaymentToNode(amountMsat, pr, routeParams = integrationTestRouteParams, maxAttempts = 1)) val paymentId = sender.expectMsgType[UUID] sender.expectMsgType[PreimageReceived] val ps = sender.expectMsgType[PaymentSent] diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/Bolt11InvoiceSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/Bolt11InvoiceSpec.scala index 40fd3764ee..27f359ad34 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/Bolt11InvoiceSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/Bolt11InvoiceSpec.scala @@ -283,7 +283,7 @@ class Bolt11InvoiceSpec extends AnyFunSuite { assert(invoice.nodeId == PublicKey(hex"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad")) assert(invoice.description == Right(Crypto.sha256(ByteVector.view("One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon".getBytes)))) assert(invoice.fallbackAddress() === Some("bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3")) - assert(invoice.minFinalCltvExpiryDelta === Some(CltvExpiryDelta(12))) + assert(invoice.minFinalCltvExpiryDelta === CltvExpiryDelta(12)) assert(invoice.tags.size == 6) assert(invoice.sign(priv).toString == ref) } @@ -347,7 +347,7 @@ class Bolt11InvoiceSpec extends AnyFunSuite { assert(invoice.description === Left("Blockstream Store: 88.85 USD for Blockstream Ledger Nano S x 1, \"Back In My Day\" Sticker x 2, \"I Got Lightning Working\" Sticker x 2 and 1 more items")) assert(invoice.fallbackAddress().isEmpty) assert(invoice.relativeExpiry === 604800.seconds) - assert(invoice.minFinalCltvExpiryDelta === Some(CltvExpiryDelta(10))) + assert(invoice.minFinalCltvExpiryDelta === CltvExpiryDelta(10)) assert(invoice.routingInfo === Seq(Seq(ExtraHop(PublicKey(hex"03d06758583bb5154774a6eb221b1276c9e82d65bbaceca806d90e20c108f4b1c7"), ShortChannelId("589390x3312x1"), 1000 msat, 2500, CltvExpiryDelta(40))))) assert(invoice.sign(priv).toString === ref) } @@ -401,7 +401,7 @@ class Bolt11InvoiceSpec extends AnyFunSuite { assert(field1 == field) val invoice = Bolt11Invoice(chainHash = Block.LivenetGenesisBlock.hash, amount = Some(123 msat), paymentHash = ByteVector32(ByteVector.fill(32)(1)), privateKey = priv, description = Left("Some invoice"), minFinalCltvExpiryDelta = CltvExpiryDelta(18), expirySeconds = Some(123456), timestamp = 12345 unixsec) - assert(invoice.minFinalCltvExpiryDelta === Some(CltvExpiryDelta(18))) + assert(invoice.minFinalCltvExpiryDelta === CltvExpiryDelta(18)) val serialized = invoice.toString val pr1 = Bolt11Invoice.fromString(serialized) assert(invoice == pr1) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/MultiPartHandlerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/MultiPartHandlerSpec.scala index 200fd7f084..802ea5a8cc 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/MultiPartHandlerSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/MultiPartHandlerSpec.scala @@ -173,12 +173,12 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike sender.send(handlerWithMpp, ReceivePayment(Some(42000 msat), Left("1 coffee"))) val pr1 = sender.expectMsgType[Invoice] - assert(pr1.minFinalCltvExpiryDelta === Some(nodeParams.channelConf.minFinalExpiryDelta)) + assert(pr1.minFinalCltvExpiryDelta === nodeParams.channelConf.minFinalExpiryDelta) assert(pr1.relativeExpiry === Alice.nodeParams.invoiceExpiry) sender.send(handlerWithMpp, ReceivePayment(Some(42000 msat), Left("1 coffee with custom expiry"), expirySeconds_opt = Some(60))) val pr2 = sender.expectMsgType[Invoice] - assert(pr2.minFinalCltvExpiryDelta === Some(nodeParams.channelConf.minFinalExpiryDelta)) + assert(pr2.minFinalCltvExpiryDelta === nodeParams.channelConf.minFinalExpiryDelta) assert(pr2.relativeExpiry === 60.seconds) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentInitiatorSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentInitiatorSpec.scala index 5dacd0a063..a66b924217 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentInitiatorSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentInitiatorSpec.scala @@ -97,14 +97,14 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike test("forward payment with user custom tlv records") { f => import f._ val customRecords = Seq(GenericTlv(500L, hex"01020304"), GenericTlv(501L, hex"d34db33f")) - val invoice = Bolt11Invoice(Block.LivenetGenesisBlock.hash, None, paymentHash, priv_c.privateKey, Left("test"), Channel.MIN_CLTV_EXPIRY_DELTA) - val req = SendPaymentToNode(finalAmount, invoice, 1, Channel.MIN_CLTV_EXPIRY_DELTA, userCustomTlvs = customRecords, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) + val invoice = Bolt11Invoice(Block.LivenetGenesisBlock.hash, None, paymentHash, priv_c.privateKey, Left("test"), Bolt11Invoice.MIN_CLTV_EXPIRY_DELTA) + val req = SendPaymentToNode(finalAmount, invoice, 1, userCustomTlvs = customRecords, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) sender.send(initiator, req) sender.expectMsgType[UUID] payFsm.expectMsgType[SendPaymentConfig] val FinalTlvPayload(tlvs) = payFsm.expectMsgType[PaymentLifecycle.SendPayment].finalPayload assert(tlvs.get[AmountToForward].get.amount == finalAmount) - assert(tlvs.get[OutgoingCltv].get.cltv == req.fallbackFinalExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight + 1)) + assert(tlvs.get[OutgoingCltv].get.cltv == req.invoice.minFinalCltvExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight + 1)) assert(tlvs.unknown == customRecords) } @@ -116,7 +116,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike payFsm.expectMsgType[SendPaymentConfig] val FinalTlvPayload(tlvs) = payFsm.expectMsgType[PaymentLifecycle.SendPayment].finalPayload assert(tlvs.get[AmountToForward].get.amount == finalAmount) - assert(tlvs.get[OutgoingCltv].get.cltv == Channel.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry(nodeParams.currentBlockHeight + 1)) + assert(tlvs.get[OutgoingCltv].get.cltv == Bolt11Invoice.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry(nodeParams.currentBlockHeight + 1)) assert(tlvs.get[KeySend].get.paymentPreimage == paymentPreimage) assert(tlvs.unknown.isEmpty) } @@ -131,7 +131,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike Bolt11Invoice.InvoiceFeatures(Features[InvoiceFeature](Map[Feature with InvoiceFeature, FeatureSupport](VariableLengthOnion -> Mandatory, PaymentSecret -> Mandatory), Set(UnknownFeature(42))).unscoped()) ) val invoice = Bolt11Invoice("lnbc", Some(finalAmount), TimestampSecond.now(), randomKey().publicKey, taggedFields, ByteVector.empty) - val req = SendPaymentToNode(finalAmount + 100.msat, invoice, 1, CltvExpiryDelta(42), routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) + val req = SendPaymentToNode(finalAmount + 100.msat, invoice, 1, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) sender.send(initiator, req) val id = sender.expectMsgType[UUID] val fail = sender.expectMsgType[PaymentFailed] @@ -142,12 +142,10 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike test("forward payment with pre-defined route") { f => import f._ - // we prioritize the invoice's finalExpiryDelta over the one from SendPaymentToRouteRequest - val ignoredFinalExpiryDelta = CltvExpiryDelta(18) val finalExpiryDelta = CltvExpiryDelta(36) val invoice = Bolt11Invoice(Block.LivenetGenesisBlock.hash, Some(finalAmount), paymentHash, priv_c.privateKey, Left("Some invoice"), finalExpiryDelta) val route = PredefinedNodeRoute(Seq(a, b, c)) - val request = SendPaymentToRoute(finalAmount, finalAmount, invoice, ignoredFinalExpiryDelta, route, None, None, None, 0 msat, CltvExpiryDelta(0), Nil) + val request = SendPaymentToRoute(finalAmount, finalAmount, invoice, route, None, None, None, 0 msat, CltvExpiryDelta(0), Nil) sender.send(initiator, request) val payment = sender.expectMsgType[SendPaymentToRouteResponse] payFsm.expectMsg(SendPaymentConfig(payment.paymentId, payment.parentId, None, paymentHash, finalAmount, c, Upstream.Local(payment.paymentId), Some(invoice), storeInDb = true, publishEvent = true, recordPathFindingMetrics = false, Nil)) @@ -171,7 +169,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike import f._ val finalExpiryDelta = CltvExpiryDelta(24) val invoice = Bolt11Invoice(Block.LivenetGenesisBlock.hash, Some(finalAmount), paymentHash, priv_c.privateKey, Left("Some MPP invoice"), finalExpiryDelta, features = featuresWithMpp) - val req = SendPaymentToNode(finalAmount, invoice, 1, /* ignored since the invoice provides it */ CltvExpiryDelta(12), routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) + val req = SendPaymentToNode(finalAmount, invoice, 1, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) assert(req.finalExpiry(nodeParams.currentBlockHeight) === (finalExpiryDelta + 1).toCltvExpiry(nodeParams.currentBlockHeight)) sender.send(initiator, req) val id = sender.expectMsgType[UUID] @@ -195,7 +193,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike test("forward multi-part payment") { f => import f._ val invoice = Bolt11Invoice(Block.LivenetGenesisBlock.hash, Some(finalAmount), paymentHash, priv_c.privateKey, Left("Some invoice"), CltvExpiryDelta(18), features = featuresWithMpp) - val req = SendPaymentToNode(finalAmount + 100.msat, invoice, 1, CltvExpiryDelta(42), routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) + val req = SendPaymentToNode(finalAmount + 100.msat, invoice, 1, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) sender.send(initiator, req) val id = sender.expectMsgType[UUID] multiPartPayFsm.expectMsg(SendPaymentConfig(id, id, None, paymentHash, finalAmount + 100.msat, c, Upstream.Local(id), Some(invoice), storeInDb = true, publishEvent = true, recordPathFindingMetrics = true, Nil)) @@ -219,7 +217,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike import f._ val invoice = Bolt11Invoice(Block.LivenetGenesisBlock.hash, Some(finalAmount), paymentHash, priv_c.privateKey, Left("Some invoice"), CltvExpiryDelta(18), features = featuresWithMpp) val route = PredefinedChannelRoute(c, Seq(channelUpdate_ab.shortChannelId, channelUpdate_bc.shortChannelId)) - val req = SendPaymentToRoute(finalAmount / 2, finalAmount, invoice, Channel.MIN_CLTV_EXPIRY_DELTA, route, None, None, None, 0 msat, CltvExpiryDelta(0), Nil) + val req = SendPaymentToRoute(finalAmount / 2, finalAmount, invoice, route, None, None, None, 0 msat, CltvExpiryDelta(0), Nil) sender.send(initiator, req) val payment = sender.expectMsgType[SendPaymentToRouteResponse] payFsm.expectMsg(SendPaymentConfig(payment.paymentId, payment.parentId, None, paymentHash, finalAmount, c, Upstream.Local(payment.paymentId), Some(invoice), storeInDb = true, publishEvent = true, recordPathFindingMetrics = false, Nil)) @@ -250,7 +248,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike val ignoredRoutingHints = List(List(ExtraHop(b, channelUpdate_bc.shortChannelId, feeBase = 10 msat, feeProportionalMillionths = 1, cltvExpiryDelta = CltvExpiryDelta(12)))) val invoice = Bolt11Invoice(Block.LivenetGenesisBlock.hash, Some(finalAmount), paymentHash, priv_c.privateKey, Left("Some phoenix invoice"), CltvExpiryDelta(9), features = featuresWithTrampoline, extraHops = ignoredRoutingHints) val trampolineFees = 21000 msat - val req = SendTrampolinePayment(finalAmount, invoice, b, Seq((trampolineFees, CltvExpiryDelta(12))), /* ignored since the invoice provides it */ CltvExpiryDelta(18), routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) + val req = SendTrampolinePayment(finalAmount, invoice, b, Seq((trampolineFees, CltvExpiryDelta(12))), routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) sender.send(initiator, req) val id = sender.expectMsgType[UUID] multiPartPayFsm.expectMsgType[SendPaymentConfig] @@ -326,7 +324,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike val routingHints = List(List(Bolt11Invoice.ExtraHop(b, channelUpdate_bc.shortChannelId, 10 msat, 100, CltvExpiryDelta(144)))) val invoice = Bolt11Invoice(Block.RegtestGenesisBlock.hash, None, paymentHash, priv_a.privateKey, Left("#abittooreckless"), CltvExpiryDelta(18), None, None, routingHints, features = featuresWithMpp) val trampolineFees = 21000 msat - val req = SendTrampolinePayment(finalAmount, invoice, b, Seq((trampolineFees, CltvExpiryDelta(12))), CltvExpiryDelta(9), routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) + val req = SendTrampolinePayment(finalAmount, invoice, b, Seq((trampolineFees, CltvExpiryDelta(12))), routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) sender.send(initiator, req) val id = sender.expectMsgType[UUID] val fail = sender.expectMsgType[PaymentFailed] @@ -342,7 +340,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike val paymentMetadata = ByteVector.fromValidHex("01" * 400) val invoice = Bolt11Invoice(Block.LivenetGenesisBlock.hash, Some(finalAmount), paymentHash, priv_c.privateKey, Left("Much payment very metadata"), CltvExpiryDelta(9), features = featuresWithTrampoline, paymentMetadata = Some(paymentMetadata)) val trampolineFees = 21000 msat - val req = SendTrampolinePayment(finalAmount, invoice, b, Seq((trampolineFees, CltvExpiryDelta(12))), CltvExpiryDelta(18), routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) + val req = SendTrampolinePayment(finalAmount, invoice, b, Seq((trampolineFees, CltvExpiryDelta(12))), routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) sender.send(initiator, req) val id = sender.expectMsgType[UUID] val fail = sender.expectMsgType[PaymentFailed] @@ -355,7 +353,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike import f._ val invoice = Bolt11Invoice(Block.LivenetGenesisBlock.hash, Some(finalAmount), paymentHash, priv_c.privateKey, Left("Some phoenix invoice"), CltvExpiryDelta(18), features = featuresWithTrampoline) val trampolineAttempts = (21000 msat, CltvExpiryDelta(12)) :: (25000 msat, CltvExpiryDelta(24)) :: Nil - val req = SendTrampolinePayment(finalAmount, invoice, b, trampolineAttempts, CltvExpiryDelta(9), routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) + val req = SendTrampolinePayment(finalAmount, invoice, b, trampolineAttempts, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) sender.send(initiator, req) val id = sender.expectMsgType[UUID] val cfg = multiPartPayFsm.expectMsgType[SendPaymentConfig] @@ -390,7 +388,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike import f._ val invoice = Bolt11Invoice(Block.LivenetGenesisBlock.hash, Some(finalAmount), paymentHash, priv_c.privateKey, Left("Some phoenix invoice"), CltvExpiryDelta(18), features = featuresWithTrampoline) val trampolineAttempts = (21000 msat, CltvExpiryDelta(12)) :: (25000 msat, CltvExpiryDelta(24)) :: Nil - val req = SendTrampolinePayment(finalAmount, invoice, b, trampolineAttempts, CltvExpiryDelta(9), routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) + val req = SendTrampolinePayment(finalAmount, invoice, b, trampolineAttempts, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) sender.send(initiator, req) sender.expectMsgType[UUID] val cfg = multiPartPayFsm.expectMsgType[SendPaymentConfig] @@ -419,7 +417,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike import f._ val invoice = Bolt11Invoice(Block.LivenetGenesisBlock.hash, Some(finalAmount), paymentHash, priv_c.privateKey, Left("Some phoenix invoice"), CltvExpiryDelta(18), features = featuresWithTrampoline) val trampolineAttempts = (21000 msat, CltvExpiryDelta(12)) :: (25000 msat, CltvExpiryDelta(24)) :: Nil - val req = SendTrampolinePayment(finalAmount, invoice, b, trampolineAttempts, CltvExpiryDelta(9), routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) + val req = SendTrampolinePayment(finalAmount, invoice, b, trampolineAttempts, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) sender.send(initiator, req) sender.expectMsgType[UUID] @@ -447,7 +445,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike val invoice = Bolt11Invoice(Block.LivenetGenesisBlock.hash, Some(finalAmount), paymentHash, priv_c.privateKey, Left("Some invoice"), CltvExpiryDelta(18)) val trampolineFees = 100 msat val route = PredefinedNodeRoute(Seq(a, b)) - val req = SendPaymentToRoute(finalAmount + trampolineFees, finalAmount, invoice, Channel.MIN_CLTV_EXPIRY_DELTA, route, None, None, None, trampolineFees, CltvExpiryDelta(144), Seq(b, c)) + val req = SendPaymentToRoute(finalAmount + trampolineFees, finalAmount, invoice, route, None, None, None, trampolineFees, CltvExpiryDelta(144), Seq(b, c)) sender.send(initiator, req) val payment = sender.expectMsgType[SendPaymentToRouteResponse] assert(payment.trampolineSecret.nonEmpty) 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 d2dabd413e..6a81f1c689 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 @@ -56,12 +56,12 @@ class PaymentLifecycleSpec extends BaseRouterSpec { val defaultAmountMsat = 142000000 msat val defaultMaxFee = 4260000 msat // 3% of defaultAmountMsat - val defaultExpiry = Channel.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry(BlockHeight(40000)) + val defaultExpiry = Bolt11Invoice.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry(BlockHeight(40000)) val defaultPaymentPreimage = randomBytes32() val defaultPaymentHash = Crypto.sha256(defaultPaymentPreimage) val defaultOrigin = Origin.LocalCold(UUID.randomUUID()) val defaultExternalId = UUID.randomUUID().toString - val defaultInvoice = Bolt11Invoice(Block.RegtestGenesisBlock.hash, None, defaultPaymentHash, priv_d, Left("test"), Channel.MIN_CLTV_EXPIRY_DELTA) + val defaultInvoice = Bolt11Invoice(Block.RegtestGenesisBlock.hash, None, defaultPaymentHash, priv_d, Left("test"), Bolt11Invoice.MIN_CLTV_EXPIRY_DELTA) val defaultRouteParams = TestConstants.Alice.nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams def defaultRouteRequest(source: PublicKey, target: PublicKey, cfg: SendPaymentConfig): RouteRequest = RouteRequest(source, target, defaultAmountMsat, defaultMaxFee, paymentContext = Some(cfg.paymentContext), routeParams = defaultRouteParams) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentPacketSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentPacketSpec.scala index 8aabb5a086..da4d896083 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentPacketSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentPacketSpec.scala @@ -398,7 +398,7 @@ object PaymentPacketSpec { val finalAmount = 42000000 msat val currentBlockCount = 400000 - val finalExpiry = CltvExpiry(currentBlockCount) + Channel.MIN_CLTV_EXPIRY_DELTA + val finalExpiry = CltvExpiry(currentBlockCount) + Bolt11Invoice.MIN_CLTV_EXPIRY_DELTA val paymentPreimage = randomBytes32() val paymentHash = Crypto.sha256(paymentPreimage) val paymentSecret = randomBytes32() diff --git a/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Payment.scala b/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Payment.scala index 68058f17e6..912d6f15f7 100644 --- a/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Payment.scala +++ b/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Payment.scala @@ -55,15 +55,15 @@ trait Payment { val sendToRoute: Route = postRequest("sendtoroute") { implicit t => withRoute { hops => - formFields(amountMsatFormParam, "recipientAmountMsat".as[MilliSatoshi].?, invoiceFormParam, "finalCltvExpiry".as[Int], "externalId".?, "parentId".as[UUID].?, + formFields(amountMsatFormParam, "recipientAmountMsat".as[MilliSatoshi].?, invoiceFormParam, "externalId".?, "parentId".as[UUID].?, "trampolineSecret".as[ByteVector32].?, "trampolineFeesMsat".as[MilliSatoshi].?, "trampolineCltvExpiry".as[Int].?, "trampolineNodes".as[List[PublicKey]](pubkeyListUnmarshaller).?) { - (amountMsat, recipientAmountMsat_opt, invoice, finalCltvExpiry, externalId_opt, parentId_opt, trampolineSecret_opt, trampolineFeesMsat_opt, trampolineCltvExpiry_opt, trampolineNodes_opt) => { + (amountMsat, recipientAmountMsat_opt, invoice, externalId_opt, parentId_opt, trampolineSecret_opt, trampolineFeesMsat_opt, trampolineCltvExpiry_opt, trampolineNodes_opt) => { val route = hops match { case Left(shortChannelIds) => PredefinedChannelRoute(invoice.nodeId, shortChannelIds) case Right(nodeIds) => PredefinedNodeRoute(nodeIds) } complete(eclairApi.sendToRoute( - amountMsat, recipientAmountMsat_opt, externalId_opt, parentId_opt, invoice, CltvExpiryDelta(finalCltvExpiry), route, trampolineSecret_opt, trampolineFeesMsat_opt, + amountMsat, recipientAmountMsat_opt, externalId_opt, parentId_opt, invoice, route, trampolineSecret_opt, trampolineFeesMsat_opt, trampolineCltvExpiry_opt.map(CltvExpiryDelta), trampolineNodes_opt.getOrElse(Nil) )) } diff --git a/eclair-node/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala b/eclair-node/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala index 642c500cfa..8b02196a9f 100644 --- a/eclair-node/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala +++ b/eclair-node/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala @@ -927,7 +927,7 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM val jsonNodes = serialization.write(expectedRoute.nodes) val eclair = mock[Eclair] - eclair.sendToRoute(any[MilliSatoshi], any[Option[MilliSatoshi]], any[Option[String]], any[Option[UUID]], any[Bolt11Invoice], any[CltvExpiryDelta], any[PredefinedNodeRoute], any[Option[ByteVector32]], any[Option[MilliSatoshi]], any[Option[CltvExpiryDelta]], any[List[PublicKey]])(any[Timeout]) returns Future.successful(payment) + eclair.sendToRoute(any[MilliSatoshi], any[Option[MilliSatoshi]], any[Option[String]], any[Option[UUID]], any[Bolt11Invoice], any[PredefinedNodeRoute], any[Option[ByteVector32]], any[Option[MilliSatoshi]], any[Option[CltvExpiryDelta]], any[List[PublicKey]])(any[Timeout]) returns Future.successful(payment) val mockService = new MockService(eclair) Post("/sendtoroute", FormData("nodeIds" -> jsonNodes, "amountMsat" -> "1234", "finalCltvExpiry" -> "190", "externalId" -> externalId, "invoice" -> pr.toString).toEntity) ~> @@ -938,7 +938,7 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM assert(handled) assert(status == OK) assert(entityAs[String] == expected) - eclair.sendToRoute(1234 msat, None, Some(externalId), None, pr, CltvExpiryDelta(190), expectedRoute, None, None, None, Nil)(any[Timeout]).wasCalled(once) + eclair.sendToRoute(1234 msat, None, Some(externalId), None, pr, expectedRoute, None, None, None, Nil)(any[Timeout]).wasCalled(once) } } @@ -950,7 +950,7 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM val csvNodes = "0217eb8243c95f5a3b7d4c5682d10de354b7007eb59b6807ae407823963c7547a9, 0242a4ae0c5bef18048fbecf995094b74bfb0f7391418d71ed394784373f41e4f3, 026ac9fcd64fb1aa1c491fc490634dc33da41d4a17b554e0adf1b32fee88ee9f28" val eclair = mock[Eclair] - eclair.sendToRoute(any[MilliSatoshi], any[Option[MilliSatoshi]], any[Option[String]], any[Option[UUID]], any[Bolt11Invoice], any[CltvExpiryDelta], any[PredefinedNodeRoute], any[Option[ByteVector32]], any[Option[MilliSatoshi]], any[Option[CltvExpiryDelta]], any[List[PublicKey]])(any[Timeout]) returns Future.successful(payment) + eclair.sendToRoute(any[MilliSatoshi], any[Option[MilliSatoshi]], any[Option[String]], any[Option[UUID]], any[Bolt11Invoice], any[PredefinedNodeRoute], any[Option[ByteVector32]], any[Option[MilliSatoshi]], any[Option[CltvExpiryDelta]], any[List[PublicKey]])(any[Timeout]) returns Future.successful(payment) val mockService = new MockService(eclair) // this test uses CSV encoded route @@ -962,7 +962,7 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM assert(handled) assert(status == OK) assert(entityAs[String] == expected) - eclair.sendToRoute(1234 msat, None, None, None, pr, CltvExpiryDelta(190), expectedRoute, None, None, None, Nil)(any[Timeout]).wasCalled(once) + eclair.sendToRoute(1234 msat, None, None, None, pr, expectedRoute, None, None, None, Nil)(any[Timeout]).wasCalled(once) } } From f879a975ee65dc10d4f129bd2ef4cd2544d4c2aa Mon Sep 17 00:00:00 2001 From: Thomas HUET Date: Tue, 1 Mar 2022 17:02:11 +0100 Subject: [PATCH 2/3] Remove minFinalCltvExpiryDelta_opt --- .../main/scala/fr/acinq/eclair/json/JsonSerializers.scala | 2 +- .../main/scala/fr/acinq/eclair/payment/Bolt11Invoice.scala | 6 ++---- .../scala/fr/acinq/eclair/json/JsonSerializersSpec.scala | 6 +++--- eclair-node/src/test/resources/api/received-expired | 2 +- eclair-node/src/test/resources/api/received-pending | 2 +- eclair-node/src/test/resources/api/received-success | 2 +- 6 files changed, 9 insertions(+), 11 deletions(-) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/json/JsonSerializers.scala b/eclair-core/src/main/scala/fr/acinq/eclair/json/JsonSerializers.scala index 85e6605d86..9fea481a2f 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/json/JsonSerializers.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/json/JsonSerializers.scala @@ -342,7 +342,7 @@ object DirectedHtlcSerializer extends ConvertClassSerializer[DirectedHtlc](h => object InvoiceSerializer extends MinimalSerializer({ case p: Bolt11Invoice => val expiry = p.relativeExpiry_opt.map(ex => JField("expiry", JLong(ex))).toSeq - val minFinalCltvExpiry = p.minFinalCltvExpiryDelta_opt.map(mfce => JField("minFinalCltvExpiry", JInt(mfce.toInt))).toSeq + val minFinalCltvExpiry = Seq(JField("minFinalCltvExpiry", JInt(p.minFinalCltvExpiryDelta.toInt))) val amount = p.amount_opt.map(msat => JField("amount", JLong(msat.toLong))).toSeq val features = JField("features", Extraction.decompose(p.features)( DefaultFormats + diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Bolt11Invoice.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Bolt11Invoice.scala index 76d22909fb..bb56379395 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Bolt11Invoice.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Bolt11Invoice.scala @@ -89,11 +89,9 @@ case class Bolt11Invoice(prefix: String, amount_opt: Option[MilliSatoshi], creat lazy val relativeExpiry: FiniteDuration = FiniteDuration(relativeExpiry_opt.getOrElse(DEFAULT_EXPIRY_SECONDS), TimeUnit.SECONDS) - lazy val minFinalCltvExpiryDelta_opt: Option[CltvExpiryDelta] = tags.collectFirst { + lazy val minFinalCltvExpiryDelta: CltvExpiryDelta = tags.collectFirst { case cltvExpiry: Bolt11Invoice.MinFinalCltvExpiry => cltvExpiry.toCltvExpiryDelta - } - - lazy val minFinalCltvExpiryDelta: CltvExpiryDelta = minFinalCltvExpiryDelta_opt.getOrElse(MIN_CLTV_EXPIRY_DELTA) + }.getOrElse(MIN_CLTV_EXPIRY_DELTA) lazy val features: Features[InvoiceFeature] = tags.collectFirst { case f: InvoiceFeatures => f.features.invoiceFeatures() }.getOrElse(Features.empty[InvoiceFeature]) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/json/JsonSerializersSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/json/JsonSerializersSpec.scala index 5699b5ef2c..cd434cad49 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/json/JsonSerializersSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/json/JsonSerializersSpec.scala @@ -170,19 +170,19 @@ class JsonSerializersSpec extends AnyFunSuite with Matchers { test("Bolt 11 invoice") { val ref = "lnbcrt50n1p0fm9cdpp5al3wvsfkc6p7fxy89eu8gm4aww9mseu9syrcqtpa4mvx42qelkwqdq9v9ekgxqrrss9qypqsqsp5wl2t45v0hj4lgud0zjxcnjccd29ts0p2kh4vpw75vnhyyzyjtjtqarpvqg33asgh3z5ghfuvhvtf39xtnu9e7aqczpgxa9quwsxkd9rnwmx06pve9awgeewxqh90dqgrhzgsqc09ek6uejr93z8puafm6gsqgrk0hy" val pr = Invoice.fromString(ref) - JsonSerializers.serialization.write(pr)(JsonSerializers.formats) shouldBe """{"prefix":"lnbcrt","timestamp":1587386125,"nodeId":"03b207771ddba774e318970e9972da2491ff8e54f777ad0528b6526773730248a0","serialized":"lnbcrt50n1p0fm9cdpp5al3wvsfkc6p7fxy89eu8gm4aww9mseu9syrcqtpa4mvx42qelkwqdq9v9ekgxqrrss9qypqsqsp5wl2t45v0hj4lgud0zjxcnjccd29ts0p2kh4vpw75vnhyyzyjtjtqarpvqg33asgh3z5ghfuvhvtf39xtnu9e7aqczpgxa9quwsxkd9rnwmx06pve9awgeewxqh90dqgrhzgsqc09ek6uejr93z8puafm6gsqgrk0hy","description":"asd","paymentHash":"efe2e64136c683e498872e78746ebd738bb867858107802c3daed86aa819fd9c","expiry":3600,"amount":5000,"features":{"activated":{"var_onion_optin":"optional","payment_secret":"optional"},"unknown":[]},"routingInfo":[]}""" + JsonSerializers.serialization.write(pr)(JsonSerializers.formats) shouldBe """{"prefix":"lnbcrt","timestamp":1587386125,"nodeId":"03b207771ddba774e318970e9972da2491ff8e54f777ad0528b6526773730248a0","serialized":"lnbcrt50n1p0fm9cdpp5al3wvsfkc6p7fxy89eu8gm4aww9mseu9syrcqtpa4mvx42qelkwqdq9v9ekgxqrrss9qypqsqsp5wl2t45v0hj4lgud0zjxcnjccd29ts0p2kh4vpw75vnhyyzyjtjtqarpvqg33asgh3z5ghfuvhvtf39xtnu9e7aqczpgxa9quwsxkd9rnwmx06pve9awgeewxqh90dqgrhzgsqc09ek6uejr93z8puafm6gsqgrk0hy","description":"asd","paymentHash":"efe2e64136c683e498872e78746ebd738bb867858107802c3daed86aa819fd9c","expiry":3600,"minFinalCltvExpiry":18,"amount":5000,"features":{"activated":{"var_onion_optin":"optional","payment_secret":"optional"},"unknown":[]},"routingInfo":[]}""" } test("Bolt 11 invoice with routing hints") { val ref = "lntb1pst2q8xpp5qysan6j5xeq97tytxf7pfr0n75na8rztqhh03glmlgsqsyuqzgnqdqqxqrrss9qy9qsqsp5qq67gcxrn2drj5p0lc6p8wgdpqwxnc2h4s9kra5489q0fqsvhumsrzjqfqnj4upt5z6hdludky9vgk4ehzmwu2dk9rcevzczw5ywstehq79c83xr5qqqkqqqqqqqqlgqqqqqeqqjqrzjqwfn3p9278ttzzpe0e00uhyxhned3j5d9acqak5emwfpflp8z2cng838tqqqqxgqqqqqqqlgqqqqqeqqjqkxs4223x2r6sat65asfp0k2pze2rswe9np9vq08waqvsp832ffgymzgx8hgzejasesfxwcw6jj93azwq9klwuzmef3llns3n95pztgqpawp7an" val pr = Invoice.fromString(ref) - JsonSerializers.serialization.write(pr)(JsonSerializers.formats) shouldBe """{"prefix":"lntb","timestamp":1622474982,"nodeId":"03e89e4c3d41dc5332c2fb6cc66d12bfb9257ba681945a242f27a08d5ad210d891","serialized":"lntb1pst2q8xpp5qysan6j5xeq97tytxf7pfr0n75na8rztqhh03glmlgsqsyuqzgnqdqqxqrrss9qy9qsqsp5qq67gcxrn2drj5p0lc6p8wgdpqwxnc2h4s9kra5489q0fqsvhumsrzjqfqnj4upt5z6hdludky9vgk4ehzmwu2dk9rcevzczw5ywstehq79c83xr5qqqkqqqqqqqqlgqqqqqeqqjqrzjqwfn3p9278ttzzpe0e00uhyxhned3j5d9acqak5emwfpflp8z2cng838tqqqqxgqqqqqqqlgqqqqqeqqjqkxs4223x2r6sat65asfp0k2pze2rswe9np9vq08waqvsp832ffgymzgx8hgzejasesfxwcw6jj93azwq9klwuzmef3llns3n95pztgqpawp7an","description":"","paymentHash":"0121d9ea5436405f2c8b327c148df3f527d38c4b05eef8a3fbfa200813801226","expiry":3600,"features":{"activated":{"var_onion_optin":"optional","payment_secret":"optional","basic_mpp":"optional"},"unknown":[]},"routingInfo":[[{"nodeId":"02413957815d05abb7fc6d885622d5cdc5b7714db1478cb05813a8474179b83c5c","shortChannelId":"1975837x88x0","feeBase":1000,"feeProportionalMillionths":100,"cltvExpiryDelta":144}],[{"nodeId":"03933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b134","shortChannelId":"1976152x25x0","feeBase":1000,"feeProportionalMillionths":100,"cltvExpiryDelta":144}]]}""" + JsonSerializers.serialization.write(pr)(JsonSerializers.formats) shouldBe """{"prefix":"lntb","timestamp":1622474982,"nodeId":"03e89e4c3d41dc5332c2fb6cc66d12bfb9257ba681945a242f27a08d5ad210d891","serialized":"lntb1pst2q8xpp5qysan6j5xeq97tytxf7pfr0n75na8rztqhh03glmlgsqsyuqzgnqdqqxqrrss9qy9qsqsp5qq67gcxrn2drj5p0lc6p8wgdpqwxnc2h4s9kra5489q0fqsvhumsrzjqfqnj4upt5z6hdludky9vgk4ehzmwu2dk9rcevzczw5ywstehq79c83xr5qqqkqqqqqqqqlgqqqqqeqqjqrzjqwfn3p9278ttzzpe0e00uhyxhned3j5d9acqak5emwfpflp8z2cng838tqqqqxgqqqqqqqlgqqqqqeqqjqkxs4223x2r6sat65asfp0k2pze2rswe9np9vq08waqvsp832ffgymzgx8hgzejasesfxwcw6jj93azwq9klwuzmef3llns3n95pztgqpawp7an","description":"","paymentHash":"0121d9ea5436405f2c8b327c148df3f527d38c4b05eef8a3fbfa200813801226","expiry":3600,"minFinalCltvExpiry":18,"features":{"activated":{"var_onion_optin":"optional","payment_secret":"optional","basic_mpp":"optional"},"unknown":[]},"routingInfo":[[{"nodeId":"02413957815d05abb7fc6d885622d5cdc5b7714db1478cb05813a8474179b83c5c","shortChannelId":"1975837x88x0","feeBase":1000,"feeProportionalMillionths":100,"cltvExpiryDelta":144}],[{"nodeId":"03933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b134","shortChannelId":"1976152x25x0","feeBase":1000,"feeProportionalMillionths":100,"cltvExpiryDelta":144}]]}""" } test("Bolt 11 invoice with metadata") { val ref = "lnbc10m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp9wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2mq8q8a04uqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66sp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q2gqqqqqqsgqy9gw6ymamd20jumvdgpfphkhp8fzhhdhycw36egcmla5vlrtrmhs9t7psfy3hkkdqzm9eq64fjg558znccds5nhsfmxveha5xe0dykgpspdha0" val pr = Invoice.fromString(ref) - JsonSerializers.serialization.write(pr)(JsonSerializers.formats) shouldBe """{"prefix":"lnbc","timestamp":1496314658,"nodeId":"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad","serialized":"lnbc10m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp9wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2mq8q8a04uqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66sp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q2gqqqqqqsgqy9gw6ymamd20jumvdgpfphkhp8fzhhdhycw36egcmla5vlrtrmhs9t7psfy3hkkdqzm9eq64fjg558znccds5nhsfmxveha5xe0dykgpspdha0","description":"payment metadata inside","paymentHash":"0001020304050607080900010203040506070809000102030405060708090102","paymentMetadata":"01fafaf0","amount":1000000000,"features":{"activated":{"var_onion_optin":"mandatory","payment_secret":"mandatory","option_payment_metadata":"mandatory"},"unknown":[]},"routingInfo":[]}""" + JsonSerializers.serialization.write(pr)(JsonSerializers.formats) shouldBe """{"prefix":"lnbc","timestamp":1496314658,"nodeId":"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad","serialized":"lnbc10m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp9wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2mq8q8a04uqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66sp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q2gqqqqqqsgqy9gw6ymamd20jumvdgpfphkhp8fzhhdhycw36egcmla5vlrtrmhs9t7psfy3hkkdqzm9eq64fjg558znccds5nhsfmxveha5xe0dykgpspdha0","description":"payment metadata inside","paymentHash":"0001020304050607080900010203040506070809000102030405060708090102","paymentMetadata":"01fafaf0","minFinalCltvExpiry":18,"amount":1000000000,"features":{"activated":{"var_onion_optin":"mandatory","payment_secret":"mandatory","option_payment_metadata":"mandatory"},"unknown":[]},"routingInfo":[]}""" } test("GlobalBalance serializer") { diff --git a/eclair-node/src/test/resources/api/received-expired b/eclair-node/src/test/resources/api/received-expired index d1d4b57414..458926d9a9 100644 --- a/eclair-node/src/test/resources/api/received-expired +++ b/eclair-node/src/test/resources/api/received-expired @@ -1 +1 @@ -{"invoice":{"prefix":"lnbc","timestamp":1496314658,"nodeId":"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad","serialized":"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp","description":"1 cup coffee","paymentHash":"0001020304050607080900010203040506070809000102030405060708090102","expiry":60,"amount":250000000,"features":{"activated":{},"unknown":[]},"routingInfo":[]},"paymentPreimage":"0100000000000000000000000000000000000000000000000000000000000000","paymentType":"Standard","createdAt":{"iso":"1970-01-01T00:00:00.042Z","unix":0},"status":{"type":"expired"}} \ No newline at end of file +{"invoice":{"prefix":"lnbc","timestamp":1496314658,"nodeId":"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad","serialized":"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp","description":"1 cup coffee","paymentHash":"0001020304050607080900010203040506070809000102030405060708090102","expiry":60,"minFinalCltvExpiry":18,"amount":250000000,"features":{"activated":{},"unknown":[]},"routingInfo":[]},"paymentPreimage":"0100000000000000000000000000000000000000000000000000000000000000","paymentType":"Standard","createdAt":{"iso":"1970-01-01T00:00:00.042Z","unix":0},"status":{"type":"expired"}} \ No newline at end of file diff --git a/eclair-node/src/test/resources/api/received-pending b/eclair-node/src/test/resources/api/received-pending index abc7fdb9f7..a1e5f50822 100644 --- a/eclair-node/src/test/resources/api/received-pending +++ b/eclair-node/src/test/resources/api/received-pending @@ -1 +1 @@ -{"invoice":{"prefix":"lnbc","timestamp":1496314658,"nodeId":"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad","serialized":"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp","description":"1 cup coffee","paymentHash":"0001020304050607080900010203040506070809000102030405060708090102","expiry":60,"amount":250000000,"features":{"activated":{},"unknown":[]},"routingInfo":[]},"paymentPreimage":"0100000000000000000000000000000000000000000000000000000000000000","paymentType":"Standard","createdAt":{"iso":"1970-01-01T00:00:00.042Z","unix":0},"status":{"type":"pending"}} \ No newline at end of file +{"invoice":{"prefix":"lnbc","timestamp":1496314658,"nodeId":"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad","serialized":"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp","description":"1 cup coffee","paymentHash":"0001020304050607080900010203040506070809000102030405060708090102","expiry":60,"minFinalCltvExpiry":18,"amount":250000000,"features":{"activated":{},"unknown":[]},"routingInfo":[]},"paymentPreimage":"0100000000000000000000000000000000000000000000000000000000000000","paymentType":"Standard","createdAt":{"iso":"1970-01-01T00:00:00.042Z","unix":0},"status":{"type":"pending"}} \ No newline at end of file diff --git a/eclair-node/src/test/resources/api/received-success b/eclair-node/src/test/resources/api/received-success index 6fdc44e62d..89b3889a2f 100644 --- a/eclair-node/src/test/resources/api/received-success +++ b/eclair-node/src/test/resources/api/received-success @@ -1 +1 @@ -{"invoice":{"prefix":"lnbc","timestamp":1496314658,"nodeId":"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad","serialized":"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp","description":"1 cup coffee","paymentHash":"0001020304050607080900010203040506070809000102030405060708090102","expiry":60,"amount":250000000,"features":{"activated":{},"unknown":[]},"routingInfo":[]},"paymentPreimage":"0100000000000000000000000000000000000000000000000000000000000000","paymentType":"Standard","createdAt":{"iso":"1970-01-01T00:00:00.042Z","unix":0},"status":{"type":"received","amount":42,"receivedAt":{"iso":"2021-10-05T13:12:23.777Z","unix":1633439543}}} \ No newline at end of file +{"invoice":{"prefix":"lnbc","timestamp":1496314658,"nodeId":"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad","serialized":"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp","description":"1 cup coffee","paymentHash":"0001020304050607080900010203040506070809000102030405060708090102","expiry":60,"minFinalCltvExpiry":18,"amount":250000000,"features":{"activated":{},"unknown":[]},"routingInfo":[]},"paymentPreimage":"0100000000000000000000000000000000000000000000000000000000000000","paymentType":"Standard","createdAt":{"iso":"1970-01-01T00:00:00.042Z","unix":0},"status":{"type":"received","amount":42,"receivedAt":{"iso":"2021-10-05T13:12:23.777Z","unix":1633439543}}} \ No newline at end of file From 607271a01c444df2a9e834ddb0c824f7e6ebad25 Mon Sep 17 00:00:00 2001 From: Thomas HUET Date: Wed, 2 Mar 2022 09:59:59 +0100 Subject: [PATCH 3/3] JSON serialization matches original invoice --- .../scala/fr/acinq/eclair/channel/Channel.scala | 5 ++++- .../fr/acinq/eclair/channel/Commitments.scala | 4 ++-- .../fr/acinq/eclair/json/JsonSerializers.scala | 8 ++++++-- .../fr/acinq/eclair/payment/Bolt11Invoice.scala | 17 ++++------------- .../eclair/payment/send/PaymentInitiator.scala | 2 +- .../fr/acinq/eclair/channel/FuzzySpec.scala | 2 +- .../channel/states/e/NormalStateSpec.scala | 6 +++--- .../acinq/eclair/json/JsonSerializersSpec.scala | 6 +++--- .../eclair/payment/PaymentInitiatorSpec.scala | 4 ++-- .../eclair/payment/PaymentLifecycleSpec.scala | 4 ++-- .../eclair/payment/PaymentPacketSpec.scala | 2 +- .../src/test/resources/api/received-expired | 2 +- .../src/test/resources/api/received-pending | 2 +- .../src/test/resources/api/received-success | 2 +- 14 files changed, 32 insertions(+), 34 deletions(-) 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 3076d5db9f..0356de161b 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 @@ -43,7 +43,7 @@ import fr.acinq.eclair.crypto.keymanager.ChannelKeyManager import fr.acinq.eclair.db.DbEventHandler.ChannelEvent.EventType import fr.acinq.eclair.db.PendingCommandsDb import fr.acinq.eclair.io.Peer -import fr.acinq.eclair.payment.PaymentSettlingOnChain +import fr.acinq.eclair.payment.{Bolt11Invoice, PaymentSettlingOnChain} import fr.acinq.eclair.payment.relay.Relayer import fr.acinq.eclair.router.Announcements import fr.acinq.eclair.transactions.Transactions.{ClosingTx, TxOwner} @@ -114,6 +114,9 @@ object Channel { // we won't exchange more than this many signatures when negotiating the closing fee val MAX_NEGOTIATION_ITERATIONS = 20 + val MIN_CLTV_EXPIRY_DELTA: CltvExpiryDelta = Bolt11Invoice.DEFAULT_MIN_CLTV_EXPIRY_DELTA + val MAX_CLTV_EXPIRY_DELTA: CltvExpiryDelta = CltvExpiryDelta(7 * 144) // one week + // since BOLT 1.1, there is a max value for the refund delay of the main commitment tx val MAX_TO_SELF_DELAY: CltvExpiryDelta = CltvExpiryDelta(2016) 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 295f0ad55b..5b483b21b5 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala @@ -25,7 +25,7 @@ import fr.acinq.eclair.channel.Helpers.Closing import fr.acinq.eclair.channel.Monitoring.Metrics import fr.acinq.eclair.crypto.keymanager.ChannelKeyManager import fr.acinq.eclair.crypto.{Generators, ShaChain} -import fr.acinq.eclair.payment.{Bolt11Invoice, OutgoingPaymentPacket} +import fr.acinq.eclair.payment.OutgoingPaymentPacket import fr.acinq.eclair.transactions.DirectedHtlc._ import fr.acinq.eclair.transactions.Transactions._ import fr.acinq.eclair.transactions._ @@ -324,7 +324,7 @@ object Commitments { return Left(ExpiryTooSmall(commitments.channelId, minimum = minExpiry, actual = cmd.cltvExpiry, blockHeight = currentHeight)) } // we don't want to use too high a refund timeout, because our funds will be locked during that time if the payment is never fulfilled - val maxExpiry = Bolt11Invoice.MAX_CLTV_EXPIRY_DELTA.toCltvExpiry(currentHeight) + val maxExpiry = Channel.MAX_CLTV_EXPIRY_DELTA.toCltvExpiry(currentHeight) if (cmd.cltvExpiry >= maxExpiry) { return Left(ExpiryTooBig(commitments.channelId, maximum = maxExpiry, actual = cmd.cltvExpiry, blockHeight = currentHeight)) } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/json/JsonSerializers.scala b/eclair-core/src/main/scala/fr/acinq/eclair/json/JsonSerializers.scala index 9fea481a2f..a563c05d7b 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/json/JsonSerializers.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/json/JsonSerializers.scala @@ -341,8 +341,12 @@ object DirectedHtlcSerializer extends ConvertClassSerializer[DirectedHtlc](h => object InvoiceSerializer extends MinimalSerializer({ case p: Bolt11Invoice => - val expiry = p.relativeExpiry_opt.map(ex => JField("expiry", JLong(ex))).toSeq - val minFinalCltvExpiry = Seq(JField("minFinalCltvExpiry", JInt(p.minFinalCltvExpiryDelta.toInt))) + val expiry = p.tags + .collectFirst { case expiry: Bolt11Invoice.Expiry => expiry.toLong } // NB: we look at fields directly because the value has a spec-defined default + .map(ex => JField("expiry", JLong(ex))).toSeq + val minFinalCltvExpiry = p.tags + .collectFirst { case cltvExpiry: Bolt11Invoice.MinFinalCltvExpiry => cltvExpiry.toCltvExpiryDelta } // NB: we look at fields directly because the value has a spec-defined default + .map(mfce => JField("minFinalCltvExpiry", JInt(mfce.toInt))).toSeq val amount = p.amount_opt.map(msat => JField("amount", JLong(msat.toLong))).toSeq val features = JField("features", Extraction.decompose(p.features)( DefaultFormats + diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Bolt11Invoice.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Bolt11Invoice.scala index bb56379395..30fa05b615 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Bolt11Invoice.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Bolt11Invoice.scala @@ -77,21 +77,13 @@ case class Bolt11Invoice(prefix: String, amount_opt: Option[MilliSatoshi], creat /** * @return the fallback address if any. It could be a script address, pubkey address, .. */ - def fallbackAddress(): Option[String] = tags.collectFirst { - case f: Bolt11Invoice.FallbackAddress => Bolt11Invoice.FallbackAddress.toAddress(f, prefix) - } + def fallbackAddress(): Option[String] = tags.collectFirst { case f: Bolt11Invoice.FallbackAddress => Bolt11Invoice.FallbackAddress.toAddress(f, prefix) } lazy val routingInfo: Seq[Seq[ExtraHop]] = tags.collect { case t: RoutingInfo => t.path } - lazy val relativeExpiry_opt: Option[Long] = tags.collectFirst { - case expiry: Bolt11Invoice.Expiry => expiry.toLong - } - - lazy val relativeExpiry: FiniteDuration = FiniteDuration(relativeExpiry_opt.getOrElse(DEFAULT_EXPIRY_SECONDS), TimeUnit.SECONDS) + lazy val relativeExpiry: FiniteDuration = FiniteDuration(tags.collectFirst { case expiry: Bolt11Invoice.Expiry => expiry.toLong }.getOrElse(DEFAULT_EXPIRY_SECONDS), TimeUnit.SECONDS) - lazy val minFinalCltvExpiryDelta: CltvExpiryDelta = tags.collectFirst { - case cltvExpiry: Bolt11Invoice.MinFinalCltvExpiry => cltvExpiry.toCltvExpiryDelta - }.getOrElse(MIN_CLTV_EXPIRY_DELTA) + lazy val minFinalCltvExpiryDelta: CltvExpiryDelta = tags.collectFirst { case cltvExpiry: Bolt11Invoice.MinFinalCltvExpiry => cltvExpiry.toCltvExpiryDelta }.getOrElse(DEFAULT_MIN_CLTV_EXPIRY_DELTA) lazy val features: Features[InvoiceFeature] = tags.collectFirst { case f: InvoiceFeatures => f.features.invoiceFeatures() }.getOrElse(Features.empty[InvoiceFeature]) @@ -134,8 +126,7 @@ case class Bolt11Invoice(prefix: String, amount_opt: Option[MilliSatoshi], creat object Bolt11Invoice { val DEFAULT_EXPIRY_SECONDS: Long = 3600 - val MIN_CLTV_EXPIRY_DELTA: CltvExpiryDelta = CltvExpiryDelta(18) - val MAX_CLTV_EXPIRY_DELTA: CltvExpiryDelta = CltvExpiryDelta(7 * 144) // one week + val DEFAULT_MIN_CLTV_EXPIRY_DELTA: CltvExpiryDelta = CltvExpiryDelta(18) val prefixes = Map( Block.RegtestGenesisBlock.hash -> "lnbcrt", diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/send/PaymentInitiator.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/send/PaymentInitiator.scala index 7b0deb2a38..cb84ba7cbf 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/send/PaymentInitiator.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/send/PaymentInitiator.scala @@ -73,7 +73,7 @@ class PaymentInitiator(nodeParams: NodeParams, outgoingPaymentFactory: PaymentIn val paymentId = UUID.randomUUID() sender() ! paymentId val paymentCfg = SendPaymentConfig(paymentId, paymentId, r.externalId, r.paymentHash, r.recipientAmount, r.recipientNodeId, Upstream.Local(paymentId), None, storeInDb = true, publishEvent = true, recordPathFindingMetrics = r.recordPathFindingMetrics, Nil) - val finalExpiry = Bolt11Invoice.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry(nodeParams.currentBlockHeight + 1) + val finalExpiry = Channel.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry(nodeParams.currentBlockHeight + 1) val finalPayload = PaymentOnion.FinalTlvPayload(TlvStream(Seq(OnionPaymentPayloadTlv.AmountToForward(r.recipientAmount), OnionPaymentPayloadTlv.OutgoingCltv(finalExpiry), OnionPaymentPayloadTlv.PaymentData(randomBytes32(), r.recipientAmount), OnionPaymentPayloadTlv.KeySend(r.paymentPreimage)), r.userCustomTlvs)) val fsm = outgoingPaymentFactory.spawnOutgoingPayment(context, paymentCfg) fsm ! PaymentLifecycle.SendPaymentToNode(self, r.recipientNodeId, finalPayload, r.maxAttempts, routeParams = r.routeParams) 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 0885c89e10..a968c65f25 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 @@ -121,7 +121,7 @@ class FuzzySpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with Channe def buildCmdAdd(paymentHash: ByteVector32, dest: PublicKey, paymentSecret: ByteVector32): CMD_ADD_HTLC = { // allow overpaying (no more than 2 times the required amount) val amount = requiredAmount + Random.nextInt(requiredAmount.toLong.toInt).msat - val expiry = (Bolt11Invoice.MIN_CLTV_EXPIRY_DELTA + 1).toCltvExpiry(currentBlockHeight = BlockHeight(400000)) + val expiry = (Channel.MIN_CLTV_EXPIRY_DELTA + 1).toCltvExpiry(currentBlockHeight = BlockHeight(400000)) OutgoingPaymentPacket.buildCommand(self, Upstream.Local(UUID.randomUUID()), paymentHash, ChannelHop(null, dest, null) :: Nil, PaymentOnion.createSinglePartPayload(amount, expiry, paymentSecret, None)).get._1 } 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 277debe04b..c63d9ade71 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 @@ -33,7 +33,7 @@ import fr.acinq.eclair.channel.publish.TxPublisher.{PublishFinalTx, PublishRepla import fr.acinq.eclair.channel.states.{ChannelStateTestsBase, ChannelStateTestsTags} import fr.acinq.eclair.crypto.Sphinx import fr.acinq.eclair.io.Peer -import fr.acinq.eclair.payment.{Bolt11Invoice, OutgoingPaymentPacket} +import fr.acinq.eclair.payment.OutgoingPaymentPacket import fr.acinq.eclair.payment.relay.Relayer._ import fr.acinq.eclair.router.Announcements import fr.acinq.eclair.transactions.DirectedHtlc.{incoming, outgoing} @@ -158,10 +158,10 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with import f._ val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - val expiryTooBig = (Bolt11Invoice.MAX_CLTV_EXPIRY_DELTA + 1).toCltvExpiry(currentBlockHeight) + val expiryTooBig = (Channel.MAX_CLTV_EXPIRY_DELTA + 1).toCltvExpiry(currentBlockHeight) val add = CMD_ADD_HTLC(sender.ref, 500000000 msat, randomBytes32(), expiryTooBig, TestConstants.emptyOnionPacket, localOrigin(sender.ref)) alice ! add - val error = ExpiryTooBig(channelId(alice), maximum = Bolt11Invoice.MAX_CLTV_EXPIRY_DELTA.toCltvExpiry(currentBlockHeight), actual = expiryTooBig, blockHeight = currentBlockHeight) + val error = ExpiryTooBig(channelId(alice), maximum = Channel.MAX_CLTV_EXPIRY_DELTA.toCltvExpiry(currentBlockHeight), actual = expiryTooBig, blockHeight = currentBlockHeight) sender.expectMsg(RES_ADD_FAILED(add, error, Some(initialState.channelUpdate))) alice2bob.expectNoMessage(200 millis) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/json/JsonSerializersSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/json/JsonSerializersSpec.scala index cd434cad49..5699b5ef2c 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/json/JsonSerializersSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/json/JsonSerializersSpec.scala @@ -170,19 +170,19 @@ class JsonSerializersSpec extends AnyFunSuite with Matchers { test("Bolt 11 invoice") { val ref = "lnbcrt50n1p0fm9cdpp5al3wvsfkc6p7fxy89eu8gm4aww9mseu9syrcqtpa4mvx42qelkwqdq9v9ekgxqrrss9qypqsqsp5wl2t45v0hj4lgud0zjxcnjccd29ts0p2kh4vpw75vnhyyzyjtjtqarpvqg33asgh3z5ghfuvhvtf39xtnu9e7aqczpgxa9quwsxkd9rnwmx06pve9awgeewxqh90dqgrhzgsqc09ek6uejr93z8puafm6gsqgrk0hy" val pr = Invoice.fromString(ref) - JsonSerializers.serialization.write(pr)(JsonSerializers.formats) shouldBe """{"prefix":"lnbcrt","timestamp":1587386125,"nodeId":"03b207771ddba774e318970e9972da2491ff8e54f777ad0528b6526773730248a0","serialized":"lnbcrt50n1p0fm9cdpp5al3wvsfkc6p7fxy89eu8gm4aww9mseu9syrcqtpa4mvx42qelkwqdq9v9ekgxqrrss9qypqsqsp5wl2t45v0hj4lgud0zjxcnjccd29ts0p2kh4vpw75vnhyyzyjtjtqarpvqg33asgh3z5ghfuvhvtf39xtnu9e7aqczpgxa9quwsxkd9rnwmx06pve9awgeewxqh90dqgrhzgsqc09ek6uejr93z8puafm6gsqgrk0hy","description":"asd","paymentHash":"efe2e64136c683e498872e78746ebd738bb867858107802c3daed86aa819fd9c","expiry":3600,"minFinalCltvExpiry":18,"amount":5000,"features":{"activated":{"var_onion_optin":"optional","payment_secret":"optional"},"unknown":[]},"routingInfo":[]}""" + JsonSerializers.serialization.write(pr)(JsonSerializers.formats) shouldBe """{"prefix":"lnbcrt","timestamp":1587386125,"nodeId":"03b207771ddba774e318970e9972da2491ff8e54f777ad0528b6526773730248a0","serialized":"lnbcrt50n1p0fm9cdpp5al3wvsfkc6p7fxy89eu8gm4aww9mseu9syrcqtpa4mvx42qelkwqdq9v9ekgxqrrss9qypqsqsp5wl2t45v0hj4lgud0zjxcnjccd29ts0p2kh4vpw75vnhyyzyjtjtqarpvqg33asgh3z5ghfuvhvtf39xtnu9e7aqczpgxa9quwsxkd9rnwmx06pve9awgeewxqh90dqgrhzgsqc09ek6uejr93z8puafm6gsqgrk0hy","description":"asd","paymentHash":"efe2e64136c683e498872e78746ebd738bb867858107802c3daed86aa819fd9c","expiry":3600,"amount":5000,"features":{"activated":{"var_onion_optin":"optional","payment_secret":"optional"},"unknown":[]},"routingInfo":[]}""" } test("Bolt 11 invoice with routing hints") { val ref = "lntb1pst2q8xpp5qysan6j5xeq97tytxf7pfr0n75na8rztqhh03glmlgsqsyuqzgnqdqqxqrrss9qy9qsqsp5qq67gcxrn2drj5p0lc6p8wgdpqwxnc2h4s9kra5489q0fqsvhumsrzjqfqnj4upt5z6hdludky9vgk4ehzmwu2dk9rcevzczw5ywstehq79c83xr5qqqkqqqqqqqqlgqqqqqeqqjqrzjqwfn3p9278ttzzpe0e00uhyxhned3j5d9acqak5emwfpflp8z2cng838tqqqqxgqqqqqqqlgqqqqqeqqjqkxs4223x2r6sat65asfp0k2pze2rswe9np9vq08waqvsp832ffgymzgx8hgzejasesfxwcw6jj93azwq9klwuzmef3llns3n95pztgqpawp7an" val pr = Invoice.fromString(ref) - JsonSerializers.serialization.write(pr)(JsonSerializers.formats) shouldBe """{"prefix":"lntb","timestamp":1622474982,"nodeId":"03e89e4c3d41dc5332c2fb6cc66d12bfb9257ba681945a242f27a08d5ad210d891","serialized":"lntb1pst2q8xpp5qysan6j5xeq97tytxf7pfr0n75na8rztqhh03glmlgsqsyuqzgnqdqqxqrrss9qy9qsqsp5qq67gcxrn2drj5p0lc6p8wgdpqwxnc2h4s9kra5489q0fqsvhumsrzjqfqnj4upt5z6hdludky9vgk4ehzmwu2dk9rcevzczw5ywstehq79c83xr5qqqkqqqqqqqqlgqqqqqeqqjqrzjqwfn3p9278ttzzpe0e00uhyxhned3j5d9acqak5emwfpflp8z2cng838tqqqqxgqqqqqqqlgqqqqqeqqjqkxs4223x2r6sat65asfp0k2pze2rswe9np9vq08waqvsp832ffgymzgx8hgzejasesfxwcw6jj93azwq9klwuzmef3llns3n95pztgqpawp7an","description":"","paymentHash":"0121d9ea5436405f2c8b327c148df3f527d38c4b05eef8a3fbfa200813801226","expiry":3600,"minFinalCltvExpiry":18,"features":{"activated":{"var_onion_optin":"optional","payment_secret":"optional","basic_mpp":"optional"},"unknown":[]},"routingInfo":[[{"nodeId":"02413957815d05abb7fc6d885622d5cdc5b7714db1478cb05813a8474179b83c5c","shortChannelId":"1975837x88x0","feeBase":1000,"feeProportionalMillionths":100,"cltvExpiryDelta":144}],[{"nodeId":"03933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b134","shortChannelId":"1976152x25x0","feeBase":1000,"feeProportionalMillionths":100,"cltvExpiryDelta":144}]]}""" + JsonSerializers.serialization.write(pr)(JsonSerializers.formats) shouldBe """{"prefix":"lntb","timestamp":1622474982,"nodeId":"03e89e4c3d41dc5332c2fb6cc66d12bfb9257ba681945a242f27a08d5ad210d891","serialized":"lntb1pst2q8xpp5qysan6j5xeq97tytxf7pfr0n75na8rztqhh03glmlgsqsyuqzgnqdqqxqrrss9qy9qsqsp5qq67gcxrn2drj5p0lc6p8wgdpqwxnc2h4s9kra5489q0fqsvhumsrzjqfqnj4upt5z6hdludky9vgk4ehzmwu2dk9rcevzczw5ywstehq79c83xr5qqqkqqqqqqqqlgqqqqqeqqjqrzjqwfn3p9278ttzzpe0e00uhyxhned3j5d9acqak5emwfpflp8z2cng838tqqqqxgqqqqqqqlgqqqqqeqqjqkxs4223x2r6sat65asfp0k2pze2rswe9np9vq08waqvsp832ffgymzgx8hgzejasesfxwcw6jj93azwq9klwuzmef3llns3n95pztgqpawp7an","description":"","paymentHash":"0121d9ea5436405f2c8b327c148df3f527d38c4b05eef8a3fbfa200813801226","expiry":3600,"features":{"activated":{"var_onion_optin":"optional","payment_secret":"optional","basic_mpp":"optional"},"unknown":[]},"routingInfo":[[{"nodeId":"02413957815d05abb7fc6d885622d5cdc5b7714db1478cb05813a8474179b83c5c","shortChannelId":"1975837x88x0","feeBase":1000,"feeProportionalMillionths":100,"cltvExpiryDelta":144}],[{"nodeId":"03933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b134","shortChannelId":"1976152x25x0","feeBase":1000,"feeProportionalMillionths":100,"cltvExpiryDelta":144}]]}""" } test("Bolt 11 invoice with metadata") { val ref = "lnbc10m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp9wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2mq8q8a04uqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66sp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q2gqqqqqqsgqy9gw6ymamd20jumvdgpfphkhp8fzhhdhycw36egcmla5vlrtrmhs9t7psfy3hkkdqzm9eq64fjg558znccds5nhsfmxveha5xe0dykgpspdha0" val pr = Invoice.fromString(ref) - JsonSerializers.serialization.write(pr)(JsonSerializers.formats) shouldBe """{"prefix":"lnbc","timestamp":1496314658,"nodeId":"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad","serialized":"lnbc10m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp9wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2mq8q8a04uqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66sp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q2gqqqqqqsgqy9gw6ymamd20jumvdgpfphkhp8fzhhdhycw36egcmla5vlrtrmhs9t7psfy3hkkdqzm9eq64fjg558znccds5nhsfmxveha5xe0dykgpspdha0","description":"payment metadata inside","paymentHash":"0001020304050607080900010203040506070809000102030405060708090102","paymentMetadata":"01fafaf0","minFinalCltvExpiry":18,"amount":1000000000,"features":{"activated":{"var_onion_optin":"mandatory","payment_secret":"mandatory","option_payment_metadata":"mandatory"},"unknown":[]},"routingInfo":[]}""" + JsonSerializers.serialization.write(pr)(JsonSerializers.formats) shouldBe """{"prefix":"lnbc","timestamp":1496314658,"nodeId":"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad","serialized":"lnbc10m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp9wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2mq8q8a04uqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66sp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q2gqqqqqqsgqy9gw6ymamd20jumvdgpfphkhp8fzhhdhycw36egcmla5vlrtrmhs9t7psfy3hkkdqzm9eq64fjg558znccds5nhsfmxveha5xe0dykgpspdha0","description":"payment metadata inside","paymentHash":"0001020304050607080900010203040506070809000102030405060708090102","paymentMetadata":"01fafaf0","amount":1000000000,"features":{"activated":{"var_onion_optin":"mandatory","payment_secret":"mandatory","option_payment_metadata":"mandatory"},"unknown":[]},"routingInfo":[]}""" } test("GlobalBalance serializer") { diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentInitiatorSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentInitiatorSpec.scala index a66b924217..68bb4500d5 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentInitiatorSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentInitiatorSpec.scala @@ -97,7 +97,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike test("forward payment with user custom tlv records") { f => import f._ val customRecords = Seq(GenericTlv(500L, hex"01020304"), GenericTlv(501L, hex"d34db33f")) - val invoice = Bolt11Invoice(Block.LivenetGenesisBlock.hash, None, paymentHash, priv_c.privateKey, Left("test"), Bolt11Invoice.MIN_CLTV_EXPIRY_DELTA) + val invoice = Bolt11Invoice(Block.LivenetGenesisBlock.hash, None, paymentHash, priv_c.privateKey, Left("test"), Channel.MIN_CLTV_EXPIRY_DELTA) val req = SendPaymentToNode(finalAmount, invoice, 1, userCustomTlvs = customRecords, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) sender.send(initiator, req) sender.expectMsgType[UUID] @@ -116,7 +116,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike payFsm.expectMsgType[SendPaymentConfig] val FinalTlvPayload(tlvs) = payFsm.expectMsgType[PaymentLifecycle.SendPayment].finalPayload assert(tlvs.get[AmountToForward].get.amount == finalAmount) - assert(tlvs.get[OutgoingCltv].get.cltv == Bolt11Invoice.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry(nodeParams.currentBlockHeight + 1)) + assert(tlvs.get[OutgoingCltv].get.cltv == Channel.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry(nodeParams.currentBlockHeight + 1)) assert(tlvs.get[KeySend].get.paymentPreimage == paymentPreimage) assert(tlvs.unknown.isEmpty) } 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 6a81f1c689..d2dabd413e 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 @@ -56,12 +56,12 @@ class PaymentLifecycleSpec extends BaseRouterSpec { val defaultAmountMsat = 142000000 msat val defaultMaxFee = 4260000 msat // 3% of defaultAmountMsat - val defaultExpiry = Bolt11Invoice.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry(BlockHeight(40000)) + val defaultExpiry = Channel.MIN_CLTV_EXPIRY_DELTA.toCltvExpiry(BlockHeight(40000)) val defaultPaymentPreimage = randomBytes32() val defaultPaymentHash = Crypto.sha256(defaultPaymentPreimage) val defaultOrigin = Origin.LocalCold(UUID.randomUUID()) val defaultExternalId = UUID.randomUUID().toString - val defaultInvoice = Bolt11Invoice(Block.RegtestGenesisBlock.hash, None, defaultPaymentHash, priv_d, Left("test"), Bolt11Invoice.MIN_CLTV_EXPIRY_DELTA) + val defaultInvoice = Bolt11Invoice(Block.RegtestGenesisBlock.hash, None, defaultPaymentHash, priv_d, Left("test"), Channel.MIN_CLTV_EXPIRY_DELTA) val defaultRouteParams = TestConstants.Alice.nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams def defaultRouteRequest(source: PublicKey, target: PublicKey, cfg: SendPaymentConfig): RouteRequest = RouteRequest(source, target, defaultAmountMsat, defaultMaxFee, paymentContext = Some(cfg.paymentContext), routeParams = defaultRouteParams) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentPacketSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentPacketSpec.scala index da4d896083..8aabb5a086 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentPacketSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentPacketSpec.scala @@ -398,7 +398,7 @@ object PaymentPacketSpec { val finalAmount = 42000000 msat val currentBlockCount = 400000 - val finalExpiry = CltvExpiry(currentBlockCount) + Bolt11Invoice.MIN_CLTV_EXPIRY_DELTA + val finalExpiry = CltvExpiry(currentBlockCount) + Channel.MIN_CLTV_EXPIRY_DELTA val paymentPreimage = randomBytes32() val paymentHash = Crypto.sha256(paymentPreimage) val paymentSecret = randomBytes32() diff --git a/eclair-node/src/test/resources/api/received-expired b/eclair-node/src/test/resources/api/received-expired index 458926d9a9..d1d4b57414 100644 --- a/eclair-node/src/test/resources/api/received-expired +++ b/eclair-node/src/test/resources/api/received-expired @@ -1 +1 @@ -{"invoice":{"prefix":"lnbc","timestamp":1496314658,"nodeId":"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad","serialized":"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp","description":"1 cup coffee","paymentHash":"0001020304050607080900010203040506070809000102030405060708090102","expiry":60,"minFinalCltvExpiry":18,"amount":250000000,"features":{"activated":{},"unknown":[]},"routingInfo":[]},"paymentPreimage":"0100000000000000000000000000000000000000000000000000000000000000","paymentType":"Standard","createdAt":{"iso":"1970-01-01T00:00:00.042Z","unix":0},"status":{"type":"expired"}} \ No newline at end of file +{"invoice":{"prefix":"lnbc","timestamp":1496314658,"nodeId":"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad","serialized":"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp","description":"1 cup coffee","paymentHash":"0001020304050607080900010203040506070809000102030405060708090102","expiry":60,"amount":250000000,"features":{"activated":{},"unknown":[]},"routingInfo":[]},"paymentPreimage":"0100000000000000000000000000000000000000000000000000000000000000","paymentType":"Standard","createdAt":{"iso":"1970-01-01T00:00:00.042Z","unix":0},"status":{"type":"expired"}} \ No newline at end of file diff --git a/eclair-node/src/test/resources/api/received-pending b/eclair-node/src/test/resources/api/received-pending index a1e5f50822..abc7fdb9f7 100644 --- a/eclair-node/src/test/resources/api/received-pending +++ b/eclair-node/src/test/resources/api/received-pending @@ -1 +1 @@ -{"invoice":{"prefix":"lnbc","timestamp":1496314658,"nodeId":"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad","serialized":"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp","description":"1 cup coffee","paymentHash":"0001020304050607080900010203040506070809000102030405060708090102","expiry":60,"minFinalCltvExpiry":18,"amount":250000000,"features":{"activated":{},"unknown":[]},"routingInfo":[]},"paymentPreimage":"0100000000000000000000000000000000000000000000000000000000000000","paymentType":"Standard","createdAt":{"iso":"1970-01-01T00:00:00.042Z","unix":0},"status":{"type":"pending"}} \ No newline at end of file +{"invoice":{"prefix":"lnbc","timestamp":1496314658,"nodeId":"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad","serialized":"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp","description":"1 cup coffee","paymentHash":"0001020304050607080900010203040506070809000102030405060708090102","expiry":60,"amount":250000000,"features":{"activated":{},"unknown":[]},"routingInfo":[]},"paymentPreimage":"0100000000000000000000000000000000000000000000000000000000000000","paymentType":"Standard","createdAt":{"iso":"1970-01-01T00:00:00.042Z","unix":0},"status":{"type":"pending"}} \ No newline at end of file diff --git a/eclair-node/src/test/resources/api/received-success b/eclair-node/src/test/resources/api/received-success index 89b3889a2f..6fdc44e62d 100644 --- a/eclair-node/src/test/resources/api/received-success +++ b/eclair-node/src/test/resources/api/received-success @@ -1 +1 @@ -{"invoice":{"prefix":"lnbc","timestamp":1496314658,"nodeId":"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad","serialized":"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp","description":"1 cup coffee","paymentHash":"0001020304050607080900010203040506070809000102030405060708090102","expiry":60,"minFinalCltvExpiry":18,"amount":250000000,"features":{"activated":{},"unknown":[]},"routingInfo":[]},"paymentPreimage":"0100000000000000000000000000000000000000000000000000000000000000","paymentType":"Standard","createdAt":{"iso":"1970-01-01T00:00:00.042Z","unix":0},"status":{"type":"received","amount":42,"receivedAt":{"iso":"2021-10-05T13:12:23.777Z","unix":1633439543}}} \ No newline at end of file +{"invoice":{"prefix":"lnbc","timestamp":1496314658,"nodeId":"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad","serialized":"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp","description":"1 cup coffee","paymentHash":"0001020304050607080900010203040506070809000102030405060708090102","expiry":60,"amount":250000000,"features":{"activated":{},"unknown":[]},"routingInfo":[]},"paymentPreimage":"0100000000000000000000000000000000000000000000000000000000000000","paymentType":"Standard","createdAt":{"iso":"1970-01-01T00:00:00.042Z","unix":0},"status":{"type":"received","amount":42,"receivedAt":{"iso":"2021-10-05T13:12:23.777Z","unix":1633439543}}} \ No newline at end of file