diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Bolt12Invoice.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Bolt12Invoice.scala index 03c4ab715d..5963479bc4 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Bolt12Invoice.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Bolt12Invoice.scala @@ -53,7 +53,7 @@ case class Bolt12Invoice(records: TlvStream[InvoiceTlv]) extends Invoice { // We add invoice features that are implicitly required for Bolt 12 (the spec doesn't allow explicitly setting them). f.add(Features.VariableLengthOnion, FeatureSupport.Mandatory).add(Features.RouteBlinding, FeatureSupport.Mandatory) } - val blindedPaths: Seq[PaymentBlindedRoute] = records.get[InvoicePaths].get.paths.zip(records.get[InvoiceBlindedPay].get.paymentInfo).map { case (route, info) => PaymentBlindedRoute(route, info) } + val blindedPaths: Seq[PaymentBlindedContactInfo] = records.get[InvoicePaths].get.paths.zip(records.get[InvoiceBlindedPay].get.paymentInfo).map { case (route, info) => PaymentBlindedContactInfo(route, info) } val fallbacks: Option[Seq[FallbackAddress]] = records.get[InvoiceFallbacks].map(_.addresses) val signature: ByteVector64 = records.get[Signature].get.signature @@ -87,9 +87,9 @@ case class Bolt12Invoice(records: TlvStream[InvoiceTlv]) extends Invoice { } -case class PaymentBlindedRoute(route: BlindedContactInfo, paymentInfo: PaymentInfo) +case class PaymentBlindedContactInfo(route: BlindedContactInfo, paymentInfo: PaymentInfo) -case class ResolvedPaymentBlindedRoute(route: BlindedRoute, paymentInfo: PaymentInfo) +case class PaymentBlindedRoute(route: BlindedRoute, paymentInfo: PaymentInfo) object Bolt12Invoice { val hrp = "lni" @@ -110,7 +110,7 @@ object Bolt12Invoice { nodeKey: PrivateKey, invoiceExpiry: FiniteDuration, features: Features[Bolt12Feature], - paths: Seq[PaymentBlindedRoute], + paths: Seq[PaymentBlindedContactInfo], additionalTlvs: Set[InvoiceTlv] = Set.empty, customTlvs: Set[GenericTlv] = Set.empty): Bolt12Invoice = { require(request.amount.nonEmpty || request.offer.amount.nonEmpty) 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 a1c1e262e4..7764f11128 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 @@ -373,7 +373,7 @@ object MultiPartHandler { case None => OfferTypes.BlindedPath(blindedRoute.route) } val paymentInfo = aggregatePaymentInfo(r.amount, dummyHops, nodeParams.channelConf.minFinalExpiryDelta) - Future.successful(PaymentBlindedRoute(contactInfo, paymentInfo)) + Future.successful(PaymentBlindedContactInfo(contactInfo, paymentInfo)) } else { implicit val timeout: Timeout = 10.seconds r.router.ask(Router.FinalizeRoute(Router.PredefinedNodeRoute(r.amount, route.nodes))).mapTo[Router.RouteResponse].map(routeResponse => { @@ -384,7 +384,7 @@ object MultiPartHandler { case None => OfferTypes.BlindedPath(blindedRoute.route) } val paymentInfo = aggregatePaymentInfo(r.amount, clearRoute.hops ++ dummyHops, nodeParams.channelConf.minFinalExpiryDelta) - PaymentBlindedRoute(contactInfo, paymentInfo) + PaymentBlindedContactInfo(contactInfo, paymentInfo) }) } })).map(paths => { diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/send/OfferPayment.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/send/OfferPayment.scala index 213300a582..7f59b6727c 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/send/OfferPayment.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/send/OfferPayment.scala @@ -25,14 +25,16 @@ import fr.acinq.eclair.crypto.Sphinx.RouteBlinding.BlindedRoute import fr.acinq.eclair.message.Postman.{OnionMessageResponse, SendMessage} import fr.acinq.eclair.message.{OnionMessages, Postman} import fr.acinq.eclair.payment.send.PaymentInitiator.SendPaymentToNode -import fr.acinq.eclair.payment.{PaymentBlindedRoute, ResolvedPaymentBlindedRoute} +import fr.acinq.eclair.payment.{Bolt12Invoice, PaymentBlindedContactInfo, PaymentBlindedRoute} import fr.acinq.eclair.router.Router import fr.acinq.eclair.router.Router.RouteParams import fr.acinq.eclair.wire.protocol.MessageOnion.{FinalPayload, InvoicePayload} -import fr.acinq.eclair.wire.protocol.OfferTypes.{BlindedPath, CompactBlindedPath, InvoiceRequest, Offer, PaymentInfo} +import fr.acinq.eclair.wire.protocol.OfferTypes._ import fr.acinq.eclair.wire.protocol.{OnionMessagePayloadTlv, TlvStream} import fr.acinq.eclair.{Features, InvoiceFeature, MilliSatoshi, NodeParams, RealShortChannelId, TimestampSecond, randomKey} +import scala.annotation.tailrec + object OfferPayment { sealed trait Failure @@ -53,7 +55,7 @@ object OfferPayment { } case class UnknownShortChannelIds(scids: Seq[RealShortChannelId]) extends Failure { - override def toString: String = s"Unknown short channel ids: $scids" + override def toString: String = s"Unknown short channel ids: ${scids.mkString(",")}" } sealed trait Command @@ -99,50 +101,44 @@ object OfferPayment { } else { val payerKey = randomKey() val request = InvoiceRequest(offer, amount, quantity, nodeParams.features.bolt12Features(), payerKey, nodeParams.chainHash) - sendInvoiceRequest(nodeParams, postman, router, paymentInitiator, context, request, payerKey, replyTo, 0, sendPaymentConfig) + val offerPayment = new OfferPayment(replyTo, nodeParams, postman, router, paymentInitiator, payerKey, request, sendPaymentConfig, context) + offerPayment.sendInvoiceRequest(attemptNumber = 0) } }) } - - def sendInvoiceRequest(nodeParams: NodeParams, - postman: typed.ActorRef[Postman.Command], - router: ActorRef, - paymentInitiator: ActorRef, - context: ActorContext[Command], - request: InvoiceRequest, - payerKey: PrivateKey, - replyTo: ActorRef, - attemptNumber: Int, - sendPaymentConfig: SendPaymentConfig): Behavior[Command] = { - val contactInfo = request.offer.contactInfos(attemptNumber % request.offer.contactInfos.length) - val messageContent = TlvStream[OnionMessagePayloadTlv](OnionMessagePayloadTlv.InvoiceRequest(request.records)) +} + +private class OfferPayment(replyTo: ActorRef, + nodeParams: NodeParams, + postman: typed.ActorRef[Postman.Command], + router: ActorRef, + paymentInitiator: ActorRef, + payerKey: PrivateKey, + invoiceRequest: InvoiceRequest, + sendPaymentConfig: OfferPayment.SendPaymentConfig, + context: ActorContext[OfferPayment.Command]) { + + import OfferPayment._ + + def sendInvoiceRequest(attemptNumber: Int): Behavior[Command] = { + val contactInfo = invoiceRequest.offer.contactInfos(attemptNumber % invoiceRequest.offer.contactInfos.length) + val messageContent = TlvStream[OnionMessagePayloadTlv](OnionMessagePayloadTlv.InvoiceRequest(invoiceRequest.records)) val routingStrategy = if (sendPaymentConfig.connectDirectly) OnionMessages.RoutingStrategy.connectDirectly else OnionMessages.RoutingStrategy.FindRoute postman ! SendMessage(contactInfo, routingStrategy, messageContent, expectsReply = true, context.messageAdapter(WrappedMessageResponse)) - waitForInvoice(nodeParams, postman, router, paymentInitiator, context, request, payerKey, replyTo, attemptNumber + 1, sendPaymentConfig) + waitForInvoice(attemptNumber + 1) } - def waitForInvoice(nodeParams: NodeParams, - postman: typed.ActorRef[Postman.Command], - router: ActorRef, - paymentInitiator: ActorRef, - context: ActorContext[Command], - request: InvoiceRequest, - payerKey: PrivateKey, - replyTo: ActorRef, - attemptNumber: Int, - sendPaymentConfig: SendPaymentConfig): Behavior[Command] = { + private def waitForInvoice(attemptNumber: Int): Behavior[Command] = { Behaviors.receiveMessagePartial { - case WrappedMessageResponse(Postman.Response(payload: InvoicePayload)) if payload.invoice.validateFor(request).isRight => - val sendPaymentToNode = SendPaymentToNode(replyTo, payload.invoice.amount, payload.invoice, Nil, maxAttempts = sendPaymentConfig.maxAttempts, externalId = sendPaymentConfig.externalId_opt, routeParams = sendPaymentConfig.routeParams, payerKey_opt = Some(payerKey), blockUntilComplete = sendPaymentConfig.blocking) - val scids = payload.invoice.blindedPaths.collect { case PaymentBlindedRoute(CompactBlindedPath(scdidDir, _, _), _) => scdidDir.scid } - resolve(context, paymentInitiator, router, sendPaymentToNode, payload.invoice.blindedPaths, Nil, scids) + case WrappedMessageResponse(Postman.Response(payload: InvoicePayload)) if payload.invoice.validateFor(invoiceRequest).isRight => + resolveCompactBlindedPaths(payload.invoice, payload.invoice.blindedPaths, Nil) case WrappedMessageResponse(Postman.Response(payload)) => // We've received a response but it is not an invoice as we expected or it is an invalid invoice. - replyTo ! InvalidInvoiceResponse(request, payload) + replyTo ! InvalidInvoiceResponse(invoiceRequest, payload) Behaviors.stopped case WrappedMessageResponse(Postman.NoReply) if attemptNumber < nodeParams.onionMessageConfig.maxAttempts => // We didn't get a response, let's retry. - sendInvoiceRequest(nodeParams, postman, router, paymentInitiator, context, request, payerKey, replyTo, attemptNumber, sendPaymentConfig) + sendInvoiceRequest(attemptNumber) case WrappedMessageResponse(_) => // We can't reach the offer node or the offer node can't reach us. replyTo ! NoInvoiceResponse @@ -150,46 +146,43 @@ object OfferPayment { } } - def resolve(context: ActorContext[Command], - paymentInitiator: ActorRef, - router: ActorRef, - sendPaymentToNode: SendPaymentToNode, - toResolve: Seq[PaymentBlindedRoute], - resolved: Seq[ResolvedPaymentBlindedRoute], - scids: Seq[RealShortChannelId]): Behavior[Command] = { + /** + * Blinded paths in Bolt 12 invoices may encode the introduction node with an scid and a direction: we need to resolve + * that to a nodeId in order to reach that introduction node and use the blinded path. + */ + @tailrec + private def resolveCompactBlindedPaths(invoice: Bolt12Invoice, toResolve: Seq[PaymentBlindedContactInfo], resolved: Seq[PaymentBlindedRoute]): Behavior[Command] = { if (toResolve.isEmpty) { if (resolved.isEmpty) { - // No route could be resolved - sendPaymentToNode.replyTo ! UnknownShortChannelIds(scids) + // We don't know how to reach any of the blinded paths' introduction nodes. + val scids = invoice.blindedPaths.collect { case PaymentBlindedContactInfo(CompactBlindedPath(scdidDir, _, _), _) => scdidDir.scid } + replyTo ! UnknownShortChannelIds(scids) } else { - paymentInitiator ! sendPaymentToNode.copy(resolvedPaths = resolved) + paymentInitiator ! SendPaymentToNode(replyTo, invoice.amount, invoice, resolved, maxAttempts = sendPaymentConfig.maxAttempts, externalId = sendPaymentConfig.externalId_opt, routeParams = sendPaymentConfig.routeParams, payerKey_opt = Some(payerKey), blockUntilComplete = sendPaymentConfig.blocking) } Behaviors.stopped } else { toResolve.head match { - case PaymentBlindedRoute(BlindedPath(route), paymentInfo) => - resolve(context, paymentInitiator, router, sendPaymentToNode, toResolve.tail, resolved :+ ResolvedPaymentBlindedRoute(route, paymentInfo), scids) - case PaymentBlindedRoute(route: CompactBlindedPath, paymentInfo) => + case PaymentBlindedContactInfo(BlindedPath(route), paymentInfo) => + resolveCompactBlindedPaths(invoice, toResolve.tail, resolved :+ PaymentBlindedRoute(route, paymentInfo)) + case PaymentBlindedContactInfo(route: CompactBlindedPath, paymentInfo) => router ! Router.GetNodeId(context.messageAdapter(WrappedNodeId), route.introductionNode.scid, route.introductionNode.isNode1) - waitForNodeId(context, paymentInitiator, router, sendPaymentToNode, route, paymentInfo, toResolve.tail, resolved, scids) + waitForNodeId(invoice, route, paymentInfo, toResolve.tail, resolved) } } } - def waitForNodeId(context: ActorContext[Command], - paymentInitiator: ActorRef, - router: ActorRef, - sendPaymentToNode: SendPaymentToNode, - compactRoute: CompactBlindedPath, - paymentInfo: PaymentInfo, - toResolve: Seq[PaymentBlindedRoute], - resolved: Seq[ResolvedPaymentBlindedRoute], - scids: Seq[RealShortChannelId]): Behavior[Command] = + private def waitForNodeId(invoice: Bolt12Invoice, + compactRoute: CompactBlindedPath, + paymentInfo: PaymentInfo, + toResolve: Seq[PaymentBlindedContactInfo], + resolved: Seq[PaymentBlindedRoute]): Behavior[Command] = Behaviors.receiveMessagePartial { case WrappedNodeId(None) => - resolve(context, paymentInitiator, router, sendPaymentToNode, toResolve, resolved, scids) + resolveCompactBlindedPaths(invoice, toResolve, resolved) case WrappedNodeId(Some(nodeId)) => - val resolvedPaymentBlindedRoute = ResolvedPaymentBlindedRoute(BlindedRoute(nodeId, compactRoute.blindingKey, compactRoute.blindedNodes), paymentInfo) - resolve(context, paymentInitiator, router, sendPaymentToNode, toResolve, resolved :+ resolvedPaymentBlindedRoute, scids) + val resolvedPaymentBlindedRoute = PaymentBlindedRoute(BlindedRoute(nodeId, compactRoute.blindingKey, compactRoute.blindedNodes), paymentInfo) + resolveCompactBlindedPaths(invoice, toResolve, resolved :+ resolvedPaymentBlindedRoute) } + } \ No newline at end of file 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 032c5184ea..806ec7fdba 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 @@ -320,7 +320,7 @@ object PaymentInitiator { case class SendPaymentToNode(replyTo: ActorRef, recipientAmount: MilliSatoshi, invoice: Invoice, - resolvedPaths: Seq[ResolvedPaymentBlindedRoute], + resolvedPaths: Seq[PaymentBlindedRoute], maxAttempts: Int, externalId: Option[String] = None, routeParams: RouteParams, @@ -384,7 +384,7 @@ object PaymentInitiator { */ case class SendPaymentToRoute(recipientAmount: MilliSatoshi, invoice: Invoice, - resolvedPaths: Seq[ResolvedPaymentBlindedRoute], + resolvedPaths: Seq[PaymentBlindedRoute], route: PredefinedRoute, externalId: Option[String], parentId: Option[UUID], diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/send/Recipient.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/send/Recipient.scala index 3eb95ef5df..75bf4436f7 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/send/Recipient.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/send/Recipient.scala @@ -21,7 +21,7 @@ import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey import fr.acinq.eclair.crypto.Sphinx import fr.acinq.eclair.payment.Invoice.ExtraEdge import fr.acinq.eclair.payment.OutgoingPaymentPacket._ -import fr.acinq.eclair.payment.{Bolt11Invoice, Bolt12Invoice, OutgoingPaymentPacket, ResolvedPaymentBlindedRoute} +import fr.acinq.eclair.payment.{Bolt11Invoice, Bolt12Invoice, OutgoingPaymentPacket, PaymentBlindedRoute} import fr.acinq.eclair.router.Router._ import fr.acinq.eclair.wire.protocol.PaymentOnion.{FinalPayload, IntermediatePayload, OutgoingBlindedPerHopPayload} import fr.acinq.eclair.wire.protocol.{GenericTlv, OnionRoutingPacket, PaymentOnionCodecs} @@ -166,7 +166,8 @@ case class BlindedRecipient(nodeId: PublicKey, } object BlindedRecipient { - def apply(invoice: Bolt12Invoice, paths: Seq[ResolvedPaymentBlindedRoute], totalAmount: MilliSatoshi, expiry: CltvExpiry, customTlvs: Set[GenericTlv]): BlindedRecipient = { + /** @param paths the caller must resolve the scid of compact blinded paths, otherwise they will be ignored. */ + def apply(invoice: Bolt12Invoice, paths: Seq[PaymentBlindedRoute], totalAmount: MilliSatoshi, expiry: CltvExpiry, customTlvs: Set[GenericTlv]): BlindedRecipient = { val blindedHops = paths.map( path => { // We don't know the scids of channels inside the blinded route, but it's useful to have an ID to refer to a diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/db/PaymentsDbSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/db/PaymentsDbSpec.scala index 81d731d2a1..1eddf80102 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/db/PaymentsDbSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/db/PaymentsDbSpec.scala @@ -801,7 +801,7 @@ object PaymentsDbSpec { def createBolt12Invoice(amount: MilliSatoshi, payerKey: PrivateKey, recipientKey: PrivateKey, preimage: ByteVector32): Bolt12Invoice = { val offer = Offer(Some(amount), "some offer", recipientKey.publicKey, Features.empty, Block.TestnetGenesisBlock.hash) val invoiceRequest = InvoiceRequest(offer, 789 msat, 1, Features.empty, payerKey, Block.TestnetGenesisBlock.hash) - val dummyRoute = PaymentBlindedRoute(BlindedPath(RouteBlinding.create(randomKey(), Seq(randomKey().publicKey), Seq(randomBytes(100))).route), PaymentInfo(0 msat, 0, CltvExpiryDelta(0), 0 msat, 0 msat, Features.empty)) + val dummyRoute = PaymentBlindedContactInfo(BlindedPath(RouteBlinding.create(randomKey(), Seq(randomKey().publicKey), Seq(randomBytes(100))).route), PaymentInfo(0 msat, 0, CltvExpiryDelta(0), 0 msat, 0 msat, Features.empty)) Bolt12Invoice(invoiceRequest, preimage, recipientKey, 1 hour, Features.empty, Seq(dummyRoute)) } } 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 2cff4c6e57..443238a798 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 @@ -828,11 +828,18 @@ class PaymentIntegrationSpec extends IntegrationSpec { } test("send to compact route") { + val probe = TestProbe() val recipientKey = randomKey() val amount = 10_000_000 msat val chain = nodes("C").nodeParams.chainHash val pathId = randomBytes32() - val offerPath = OfferTypes.BlindedPath(buildRoute(randomKey(), Seq(IntermediateNode(nodes("B").nodeParams.nodeId), IntermediateNode(nodes("C").nodeParams.nodeId)), Recipient(nodes("C").nodeParams.nodeId, Some(pathId)))) + val scidDirEB = { + probe.send(nodes("B").router, Router.GetChannels) + val Some(channelBE) = probe.expectMsgType[Iterable[ChannelAnnouncement]].find(ann => Set(ann.nodeId1, ann.nodeId2) == Set(nodes("B").nodeParams.nodeId, nodes("E").nodeParams.nodeId)) + ShortChannelIdDir(channelBE.nodeId1 == nodes("B").nodeParams.nodeId, channelBE.shortChannelId) + } + val offerBlindedRoute = buildRoute(randomKey(), Seq(IntermediateNode(nodes("B").nodeParams.nodeId), IntermediateNode(nodes("C").nodeParams.nodeId)), Recipient(nodes("C").nodeParams.nodeId, Some(pathId))) + val offerPath = OfferTypes.CompactBlindedPath(scidDirEB, offerBlindedRoute.blindingKey, offerBlindedRoute.blindedNodes) val offer = Offer(Some(amount), "test offer", recipientKey.publicKey, nodes("C").nodeParams.features.bolt12Features(), chain, additionalTlvs = Set(OfferPaths(Seq(offerPath)))) val offerHandler = TypedProbe[HandlerCommand]()(nodes("C").system.toTyped) nodes("C").offerManager ! RegisterOffer(offer, recipientKey, Some(pathId), offerHandler.ref) @@ -842,18 +849,18 @@ class PaymentIntegrationSpec extends IntegrationSpec { alice.payOfferBlocking(offer, amount, 1, maxAttempts_opt = Some(3))(30 seconds).pipeTo(sender.ref) val handleInvoiceRequest = offerHandler.expectMessageType[HandleInvoiceRequest] - val probe = TestProbe() - probe.send(nodes("C").router, Router.GetChannels) - val b = nodes("B").nodeParams.nodeId - val channelWithB = probe.expectMsgType[Iterable[ChannelAnnouncement]].find(ann => ann.nodeId1 == b || ann.nodeId2 == b).get + val scidDirCB = { + probe.send(nodes("B").router, Router.GetChannels) + val Some(channelBC) = probe.expectMsgType[Iterable[ChannelAnnouncement]].find(ann => Set(ann.nodeId1, ann.nodeId2) == Set(nodes("B").nodeParams.nodeId, nodes("C").nodeParams.nodeId)) + ShortChannelIdDir(channelBC.nodeId1 == nodes("B").nodeParams.nodeId, channelBC.shortChannelId) + } val receivingRoutes = Seq( - ReceivingRoute(Seq(nodes("B").nodeParams.nodeId, nodes("C").nodeParams.nodeId), CltvExpiryDelta(555), Seq(DummyBlindedHop(55 msat, 55, CltvExpiryDelta(55))), Some(ShortChannelIdDir(channelWithB.nodeId1 == b, channelWithB.shortChannelId))) + ReceivingRoute(Seq(nodes("B").nodeParams.nodeId, nodes("C").nodeParams.nodeId), CltvExpiryDelta(555), Seq(DummyBlindedHop(55 msat, 55, CltvExpiryDelta(55))), Some(scidDirCB)) ) - handleInvoiceRequest.replyTo ! InvoiceRequestActor.ApproveRequest(amount, receivingRoutes, pluginData_opt = Some(hex"eff0")) + handleInvoiceRequest.replyTo ! InvoiceRequestActor.ApproveRequest(amount, receivingRoutes) val handlePayment = offerHandler.expectMessageType[HandlePayment] assert(handlePayment.offerId == offer.offerId) - assert(handlePayment.pluginData_opt.contains(hex"eff0")) handlePayment.replyTo ! PaymentActor.AcceptPayment() val paymentSent = sender.expectMsgType[PaymentSent] diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/message/PostmanSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/message/PostmanSpec.scala index 9956d6864d..57c946d124 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/message/PostmanSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/message/PostmanSpec.scala @@ -41,6 +41,8 @@ import org.scalatest.Outcome import org.scalatest.funsuite.FixtureAnyFunSuiteLike import scodec.bits.HexStringSyntax +import scala.concurrent.duration.DurationInt + class PostmanSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("application")) with FixtureAnyFunSuiteLike { case class FixtureParam(postman: ActorRef[Command], nodeParams: NodeParams, messageSender: TestProbe[OnionMessageResponse], switchboard: TestProbe[Any], offerManager: TestProbe[RequestInvoice], router: TestProbe[Router.PostmanRequest]) @@ -94,7 +96,7 @@ class PostmanSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("applicat testKit.system.eventStream ! EventStream.Publish(ReceiveMessage(replyPayload)) messageSender.expectMessage(Response(replyPayload)) - messageSender.expectNoMessage() + messageSender.expectNoMessage(10 millis) } test("sending failure") { f => @@ -113,7 +115,7 @@ class PostmanSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("applicat replyTo ! Disconnected(messageId) messageSender.expectMessage(MessageFailed("Peer is not connected")) - messageSender.expectNoMessage() + messageSender.expectNoMessage(10 millis) } test("timeout") { f => @@ -140,7 +142,7 @@ class PostmanSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("applicat val ReceiveMessage(replyPayload) = OnionMessages.process(nodeParams.privateKey, reply) testKit.system.eventStream ! EventStream.Publish(ReceiveMessage(replyPayload)) - messageSender.expectNoMessage() + messageSender.expectNoMessage(10 millis) } test("do not expect reply") { f => @@ -162,7 +164,7 @@ class PostmanSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("applicat assert(finalPayload.records.get[ReplyPath].isEmpty) messageSender.expectMessage(MessageSent) - messageSender.expectNoMessage() + messageSender.expectNoMessage(10 millis) } test("send to route that starts at ourselves") { f => @@ -180,7 +182,7 @@ class PostmanSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("applicat assert(finalPayload.records.get[ReplyPath].isEmpty) messageSender.expectMessage(MessageSent) - messageSender.expectNoMessage() + messageSender.expectNoMessage(10 millis) } test("forward invoice request to offer manager") { f => @@ -269,7 +271,7 @@ class PostmanSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("applicat assert(finalPayload.records.get[ReplyPath].isEmpty) messageSender.expectMessage(MessageSent) - messageSender.expectNoMessage() + messageSender.expectNoMessage(10 millis) } test("send to compact route that starts at ourselves") { f => @@ -298,6 +300,6 @@ class PostmanSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("applicat assert(finalPayload.records.get[ReplyPath].isEmpty) messageSender.expectMessage(MessageSent) - messageSender.expectNoMessage() + messageSender.expectNoMessage(10 millis) } } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/Bolt12InvoiceSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/Bolt12InvoiceSpec.scala index 99d6d9c3c2..b9538c7ab5 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/Bolt12InvoiceSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/Bolt12InvoiceSpec.scala @@ -49,9 +49,9 @@ class Bolt12InvoiceSpec extends AnyFunSuite { signedInvoice } - def createPaymentBlindedRoute(nodeId: PublicKey, sessionKey: PrivateKey = randomKey(), pathId: ByteVector = randomBytes32()): PaymentBlindedRoute = { + def createPaymentBlindedRoute(nodeId: PublicKey, sessionKey: PrivateKey = randomKey(), pathId: ByteVector = randomBytes32()): PaymentBlindedContactInfo = { val selfPayload = blindedRouteDataCodec.encode(TlvStream(PathId(pathId), PaymentConstraints(CltvExpiry(1234567), 0 msat), AllowedFeatures(Features.empty))).require.bytes - PaymentBlindedRoute(OfferTypes.BlindedPath(Sphinx.RouteBlinding.create(sessionKey, Seq(nodeId), Seq(selfPayload)).route), PaymentInfo(1 msat, 2, CltvExpiryDelta(3), 4 msat, 5 msat, Features.empty)) + PaymentBlindedContactInfo(OfferTypes.BlindedPath(Sphinx.RouteBlinding.create(sessionKey, Seq(nodeId), Seq(selfPayload)).route), PaymentInfo(1 msat, 2, CltvExpiryDelta(3), 4 msat, 5 msat, Features.empty)) } test("check invoice signature") { 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 6509d5350c..ef8fd8df15 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 @@ -298,14 +298,14 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike val invoiceRequest = InvoiceRequest(offer, finalAmount, 1, features, randomKey(), Block.RegtestGenesisBlock.hash) val blindedRoute = OfferTypes.BlindedPath(BlindedRouteCreation.createBlindedRouteWithoutHops(e, hex"2a2a2a2a", 1 msat, CltvExpiry(500_000)).route) val paymentInfo = OfferTypes.PaymentInfo(1_000 msat, 0, CltvExpiryDelta(24), 0 msat, finalAmount, Features.empty) - Bolt12Invoice(invoiceRequest, paymentPreimage, priv_e.privateKey, 300 seconds, features, Seq(PaymentBlindedRoute(blindedRoute, paymentInfo))) + Bolt12Invoice(invoiceRequest, paymentPreimage, priv_e.privateKey, 300 seconds, features, Seq(PaymentBlindedContactInfo(blindedRoute, paymentInfo))) } test("forward single-part blinded payment") { f => import f._ val payerKey = randomKey() val invoice = createBolt12Invoice(Features.empty, payerKey) - val resolvedPaths = invoice.blindedPaths.map(path => ResolvedPaymentBlindedRoute(path.route.asInstanceOf[BlindedPath].route, path.paymentInfo)) + val resolvedPaths = invoice.blindedPaths.map(path => PaymentBlindedRoute(path.route.asInstanceOf[BlindedPath].route, path.paymentInfo)) val req = SendPaymentToNode(sender.ref, finalAmount, invoice, resolvedPaths, 1, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams, payerKey_opt = Some(payerKey)) sender.send(initiator, req) val id = sender.expectMsgType[UUID] @@ -336,7 +336,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike import f._ val payerKey = randomKey() val invoice = createBolt12Invoice(Features(BasicMultiPartPayment -> Optional), payerKey) - val resolvedPaths = invoice.blindedPaths.map(path => ResolvedPaymentBlindedRoute(path.route.asInstanceOf[BlindedPath].route, path.paymentInfo)) + val resolvedPaths = invoice.blindedPaths.map(path => PaymentBlindedRoute(path.route.asInstanceOf[BlindedPath].route, path.paymentInfo)) val req = SendPaymentToNode(sender.ref, finalAmount, invoice, resolvedPaths, 1, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams, payerKey_opt = Some(payerKey)) sender.send(initiator, req) val id = sender.expectMsgType[UUID] @@ -365,7 +365,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike test("reject blinded payment when route blinding deactivated", Tag(Tags.DisableRouteBlinding)) { f => import f._ val invoice = createBolt12Invoice(Features(BasicMultiPartPayment -> Optional), randomKey()) - val resolvedPaths = invoice.blindedPaths.map(path => ResolvedPaymentBlindedRoute(path.route.asInstanceOf[BlindedPath].route, path.paymentInfo)) + val resolvedPaths = invoice.blindedPaths.map(path => PaymentBlindedRoute(path.route.asInstanceOf[BlindedPath].route, path.paymentInfo)) val req = SendPaymentToNode(sender.ref, finalAmount, invoice, resolvedPaths, 1, routeParams = nodeParams.routerConf.pathFindingExperimentConf.getRandomConf().getDefaultRouteParams) sender.send(initiator, req) val id = sender.expectMsgType[UUID] 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 5893a57361..128b360929 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 @@ -219,8 +219,8 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll { val invoiceRequest = InvoiceRequest(offer, amount_bc, 1, features, randomKey(), Block.RegtestGenesisBlock.hash) val blindedRoute = OfferTypes.BlindedPath(BlindedRouteCreation.createBlindedRouteWithoutHops(c, hex"deadbeef", 1 msat, CltvExpiry(500_000)).route) val paymentInfo = PaymentInfo(0 msat, 0, CltvExpiryDelta(0), 1 msat, amount_bc, Features.empty) - val invoice = Bolt12Invoice(invoiceRequest, paymentPreimage, recipientKey, 300 seconds, features, Seq(PaymentBlindedRoute(blindedRoute, paymentInfo))) - val resolvedPaths = invoice.blindedPaths.map(path => ResolvedPaymentBlindedRoute(path.route.asInstanceOf[BlindedPath].route, path.paymentInfo)) + val invoice = Bolt12Invoice(invoiceRequest, paymentPreimage, recipientKey, 300 seconds, features, Seq(PaymentBlindedContactInfo(blindedRoute, paymentInfo))) + val resolvedPaths = invoice.blindedPaths.map(path => PaymentBlindedRoute(path.route.asInstanceOf[BlindedPath].route, path.paymentInfo)) val recipient = BlindedRecipient(invoice, resolvedPaths, amount_bc, expiry_bc, Set.empty) val hops = Seq(channelHopFromUpdate(a, b, channelUpdate_ab), channelHopFromUpdate(b, c, channelUpdate_bc)) val Right(payment) = buildOutgoingPayment(ActorRef.noSender, priv_a.privateKey, Upstream.Local(UUID.randomUUID()), paymentHash, Route(amount_bc, hops, Some(recipient.blindedHops.head)), recipient) @@ -471,8 +471,8 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll { val tmpBlindedRoute = BlindedRouteCreation.createBlindedRouteFromHops(Seq(channelHopFromUpdate(b, c, channelUpdate_bc)), hex"deadbeef", 1 msat, CltvExpiry(500_000)).route val blindedRoute = OfferTypes.BlindedPath(tmpBlindedRoute.copy(blindedNodes = tmpBlindedRoute.blindedNodes.reverse)) val paymentInfo = OfferTypes.PaymentInfo(fee_b, 0, channelUpdate_bc.cltvExpiryDelta, 0 msat, amount_bc, Features.empty) - val invoice = Bolt12Invoice(invoiceRequest, paymentPreimage, priv_c.privateKey, 300 seconds, features, Seq(PaymentBlindedRoute(blindedRoute, paymentInfo))) - val resolvedPaths = invoice.blindedPaths.map(path => ResolvedPaymentBlindedRoute(path.route.asInstanceOf[BlindedPath].route, path.paymentInfo)) + val invoice = Bolt12Invoice(invoiceRequest, paymentPreimage, priv_c.privateKey, 300 seconds, features, Seq(PaymentBlindedContactInfo(blindedRoute, paymentInfo))) + val resolvedPaths = invoice.blindedPaths.map(path => PaymentBlindedRoute(path.route.asInstanceOf[BlindedPath].route, path.paymentInfo)) val recipient = BlindedRecipient(invoice, resolvedPaths, amount_bc, expiry_bc, Set.empty) val route = Route(amount_bc, Seq(channelHopFromUpdate(a, b, channelUpdate_ab)), Some(recipient.blindedHops.head)) (route, recipient) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/send/OfferPaymentSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/send/OfferPaymentSpec.scala index 100474c164..b7b0f5a2f3 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/send/OfferPaymentSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/send/OfferPaymentSpec.scala @@ -26,7 +26,7 @@ import fr.acinq.eclair.message.OnionMessages.RoutingStrategy.FindRoute import fr.acinq.eclair.message.Postman import fr.acinq.eclair.payment.send.OfferPayment._ import fr.acinq.eclair.payment.send.PaymentInitiator.SendPaymentToNode -import fr.acinq.eclair.payment.{Bolt12Invoice, PaymentBlindedRoute} +import fr.acinq.eclair.payment.{Bolt12Invoice, PaymentBlindedContactInfo} import fr.acinq.eclair.router.Router import fr.acinq.eclair.router.Router.RouteParams import fr.acinq.eclair.wire.protocol.MessageOnion.InvoicePayload @@ -74,7 +74,7 @@ class OfferPaymentSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("app val Right(invoiceRequest) = InvoiceRequest.validate(message.get[OnionMessagePayloadTlv.InvoiceRequest].get.tlvs) val preimage = randomBytes32() - val paymentRoute = PaymentBlindedRoute(OfferTypes.BlindedPath(RouteBlinding.create(randomKey(), Seq(merchantKey.publicKey), Seq(hex"7777")).route), PaymentInfo(0 msat, 0, CltvExpiryDelta(0), 0 msat, 1_000_000_000 msat, Features.empty)) + val paymentRoute = PaymentBlindedContactInfo(OfferTypes.BlindedPath(RouteBlinding.create(randomKey(), Seq(merchantKey.publicKey), Seq(hex"7777")).route), PaymentInfo(0 msat, 0, CltvExpiryDelta(0), 0 msat, 1_000_000_000 msat, Features.empty)) val invoice = Bolt12Invoice(invoiceRequest, preimage, merchantKey, 1 minute, Features.empty, Seq(paymentRoute)) replyTo ! Postman.Response(InvoicePayload(TlvStream(OnionMessagePayloadTlv.Invoice(invoice.records)), TlvStream.empty)) val send = paymentInitiator.expectMsgType[SendPaymentToNode] @@ -121,7 +121,7 @@ class OfferPaymentSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("app val Right(invoiceRequest) = InvoiceRequest.validate(message.get[OnionMessagePayloadTlv.InvoiceRequest].get.tlvs) val preimage = randomBytes32() - val paymentRoute = PaymentBlindedRoute(OfferTypes.BlindedPath(RouteBlinding.create(randomKey(), Seq(merchantKey.publicKey), Seq(hex"7777")).route), PaymentInfo(0 msat, 0, CltvExpiryDelta(0), 0 msat, 1_000_000_000 msat, Features.empty)) + val paymentRoute = PaymentBlindedContactInfo(OfferTypes.BlindedPath(RouteBlinding.create(randomKey(), Seq(merchantKey.publicKey), Seq(hex"7777")).route), PaymentInfo(0 msat, 0, CltvExpiryDelta(0), 0 msat, 1_000_000_000 msat, Features.empty)) val invoice = Bolt12Invoice(invoiceRequest, preimage, randomKey(), 1 minute, Features.empty, Seq(paymentRoute)) replyTo ! Postman.Response(InvoicePayload(TlvStream(OnionMessagePayloadTlv.Invoice(invoice.records)), TlvStream.empty)) @@ -148,12 +148,12 @@ class OfferPaymentSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("app val preimage = randomBytes32() val blindedRoutes = Seq.fill(6)(RouteBlinding.create(randomKey(), Seq.fill(3)(randomKey().publicKey), Seq.fill(3)(randomBytes(10))).route) val paymentRoutes = Seq( - PaymentBlindedRoute(OfferTypes.BlindedPath(blindedRoutes(0)), PaymentInfo(0 msat, 0, CltvExpiryDelta(0), 0 msat, 1_000_000_000 msat, Features.empty)), - PaymentBlindedRoute(OfferTypes.CompactBlindedPath(ShortChannelIdDir(isNode1 = true, RealShortChannelId(11111)), blindedRoutes(1).blindingKey, blindedRoutes(1).blindedNodes), PaymentInfo(1 msat, 11, CltvExpiryDelta(111), 0 msat, 1_000_000_000 msat, Features.empty)), - PaymentBlindedRoute(OfferTypes.BlindedPath(blindedRoutes(2)), PaymentInfo(2 msat, 22, CltvExpiryDelta(222), 0 msat, 1_000_000_000 msat, Features.empty)), - PaymentBlindedRoute(OfferTypes.CompactBlindedPath(ShortChannelIdDir(isNode1 = false, RealShortChannelId(33333)), blindedRoutes(3).blindingKey, blindedRoutes(3).blindedNodes), PaymentInfo(3 msat, 33, CltvExpiryDelta(333), 0 msat, 1_000_000_000 msat, Features.empty)), - PaymentBlindedRoute(OfferTypes.CompactBlindedPath(ShortChannelIdDir(isNode1 = false, RealShortChannelId(44444)), blindedRoutes(4).blindingKey, blindedRoutes(4).blindedNodes), PaymentInfo(4 msat, 44, CltvExpiryDelta(444), 0 msat, 1_000_000_000 msat, Features.empty)), - PaymentBlindedRoute(OfferTypes.BlindedPath(blindedRoutes(5)), PaymentInfo(5 msat, 55, CltvExpiryDelta(555), 0 msat, 1_000_000_000 msat, Features.empty)), + PaymentBlindedContactInfo(OfferTypes.BlindedPath(blindedRoutes(0)), PaymentInfo(0 msat, 0, CltvExpiryDelta(0), 0 msat, 1_000_000_000 msat, Features.empty)), + PaymentBlindedContactInfo(OfferTypes.CompactBlindedPath(ShortChannelIdDir(isNode1 = true, RealShortChannelId(11111)), blindedRoutes(1).blindingKey, blindedRoutes(1).blindedNodes), PaymentInfo(1 msat, 11, CltvExpiryDelta(111), 0 msat, 1_000_000_000 msat, Features.empty)), + PaymentBlindedContactInfo(OfferTypes.BlindedPath(blindedRoutes(2)), PaymentInfo(2 msat, 22, CltvExpiryDelta(222), 0 msat, 1_000_000_000 msat, Features.empty)), + PaymentBlindedContactInfo(OfferTypes.CompactBlindedPath(ShortChannelIdDir(isNode1 = false, RealShortChannelId(33333)), blindedRoutes(3).blindingKey, blindedRoutes(3).blindedNodes), PaymentInfo(3 msat, 33, CltvExpiryDelta(333), 0 msat, 1_000_000_000 msat, Features.empty)), + PaymentBlindedContactInfo(OfferTypes.CompactBlindedPath(ShortChannelIdDir(isNode1 = false, RealShortChannelId(44444)), blindedRoutes(4).blindingKey, blindedRoutes(4).blindedNodes), PaymentInfo(4 msat, 44, CltvExpiryDelta(444), 0 msat, 1_000_000_000 msat, Features.empty)), + PaymentBlindedContactInfo(OfferTypes.BlindedPath(blindedRoutes(5)), PaymentInfo(5 msat, 55, CltvExpiryDelta(555), 0 msat, 1_000_000_000 msat, Features.empty)), ) val invoice = Bolt12Invoice(invoiceRequest, preimage, merchantKey, 1 minute, Features.empty, paymentRoutes) replyTo ! Postman.Response(InvoicePayload(TlvStream(OnionMessagePayloadTlv.Invoice(invoice.records)), TlvStream.empty)) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/BaseRouterSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/BaseRouterSpec.scala index d69212665b..633cf0bea9 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/BaseRouterSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/BaseRouterSpec.scala @@ -31,7 +31,7 @@ import fr.acinq.eclair.crypto.TransportHandler import fr.acinq.eclair.crypto.keymanager.{LocalChannelKeyManager, LocalNodeKeyManager} import fr.acinq.eclair.io.Peer.PeerRoutingMessage import fr.acinq.eclair.payment.send.BlindedRecipient -import fr.acinq.eclair.payment.{Bolt12Invoice, PaymentBlindedRoute, ResolvedPaymentBlindedRoute} +import fr.acinq.eclair.payment.{Bolt12Invoice, PaymentBlindedContactInfo, PaymentBlindedRoute} import fr.acinq.eclair.router.Announcements._ import fr.acinq.eclair.router.BaseRouterSpec.channelAnnouncement import fr.acinq.eclair.router.Router._ @@ -272,10 +272,10 @@ object BaseRouterSpec { val blindedRoutes = paths.map(hops => { val blindedRoute = OfferTypes.BlindedPath(BlindedRouteCreation.createBlindedRouteFromHops(hops, pathId, 1 msat, routeExpiry).route) val paymentInfo = BlindedRouteCreation.aggregatePaymentInfo(amount, hops, Channel.MIN_CLTV_EXPIRY_DELTA) - PaymentBlindedRoute(blindedRoute, paymentInfo) + PaymentBlindedContactInfo(blindedRoute, paymentInfo) }) val invoice = Bolt12Invoice(invoiceRequest, preimage, recipientKey, 300 seconds, features, blindedRoutes) - val resolvedPaths = invoice.blindedPaths.map(path => ResolvedPaymentBlindedRoute(path.route.asInstanceOf[BlindedPath].route, path.paymentInfo)) + val resolvedPaths = invoice.blindedPaths.map(path => PaymentBlindedRoute(path.route.asInstanceOf[BlindedPath].route, path.paymentInfo)) val recipient = BlindedRecipient(invoice, resolvedPaths, amount, expiry, Set.empty) (invoice, recipient) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/MessageOnionCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/MessageOnionCodecsSpec.scala index 58b5d65e27..84c186298b 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/MessageOnionCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/MessageOnionCodecsSpec.scala @@ -4,7 +4,7 @@ import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey} import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32} import fr.acinq.eclair.crypto.Sphinx import fr.acinq.eclair.crypto.Sphinx.RouteBlinding -import fr.acinq.eclair.payment.{Bolt12Invoice, PaymentBlindedRoute} +import fr.acinq.eclair.payment.{Bolt12Invoice, PaymentBlindedContactInfo} import fr.acinq.eclair.wire.protocol.MessageOnion.{FinalPayload, IntermediatePayload, InvalidResponsePayload, InvoiceErrorPayload, InvoicePayload, InvoiceRequestPayload} import fr.acinq.eclair.wire.protocol.MessageOnionCodecs._ import fr.acinq.eclair.wire.protocol.OfferTypes.PaymentInfo @@ -95,7 +95,7 @@ class MessageOnionCodecsSpec extends AnyFunSuiteLike { val payerKey = randomKey() val request = OfferTypes.InvoiceRequest(offer, 100_000 msat, 1, Features.empty, payerKey, Block.LivenetGenesisBlock.hash) val selfPayload = blindedRouteDataCodec.encode(TlvStream(PathId(randomBytes32()), PaymentConstraints(CltvExpiry(1234567), 0 msat), AllowedFeatures(Features.empty))).require.bytes - val route = PaymentBlindedRoute(OfferTypes.BlindedPath(Sphinx.RouteBlinding.create(randomKey(), Seq(nodeKey.publicKey), Seq(selfPayload)).route), PaymentInfo(1 msat, 2, CltvExpiryDelta(3), 4 msat, 5 msat, Features.empty)) + val route = PaymentBlindedContactInfo(OfferTypes.BlindedPath(Sphinx.RouteBlinding.create(randomKey(), Seq(nodeKey.publicKey), Seq(selfPayload)).route), PaymentInfo(1 msat, 2, CltvExpiryDelta(3), 4 msat, 5 msat, Features.empty)) val invoice = Bolt12Invoice(request, randomBytes32(), nodeKey, 300 seconds, Features.empty, Seq(route)) val testCasesInvalid = Seq[TlvStream[OnionMessagePayloadTlv]](