From 823230fa01dbaa242104b409b6024ba6e6d1341f Mon Sep 17 00:00:00 2001 From: Thomas HUET Date: Fri, 12 Jul 2024 20:25:57 +0200 Subject: [PATCH 1/3] Failing test --- .../lightning/payment/OutgoingPaymentHandler.kt | 4 ++-- .../payment/OutgoingPaymentHandlerTestsCommon.kt | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/commonMain/kotlin/fr/acinq/lightning/payment/OutgoingPaymentHandler.kt b/src/commonMain/kotlin/fr/acinq/lightning/payment/OutgoingPaymentHandler.kt index 8c0c3d042..c370e63a4 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/payment/OutgoingPaymentHandler.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/payment/OutgoingPaymentHandler.kt @@ -95,7 +95,7 @@ class OutgoingPaymentHandler(val nodeParams: NodeParams, val walletParams: Walle } } - private fun createChildPayments(request: PayInvoice, routes: List, trampolinePayload: PaymentAttempt.TrampolinePayload): List> { + fun createChildPayments(request: PayInvoice, routes: List, trampolinePayload: PaymentAttempt.TrampolinePayload): List> { val logger = MDCLogger(logger, staticMdc = request.mdc()) val childPayments = routes.map { createOutgoingPart(request, it, trampolinePayload) } childToParentId.putAll(childPayments.map { it.first.id to request.paymentId }) @@ -337,7 +337,7 @@ class OutgoingPaymentHandler(val nodeParams: NodeParams, val walletParams: Walle return Triple(outgoingPayment, secrets, WrappedChannelCommand(route.channel.channelId, cmdAdd)) } - private fun createTrampolinePayload(request: PayInvoice, fees: TrampolineFees, currentBlockHeight: Int): Triple { + fun createTrampolinePayload(request: PayInvoice, fees: TrampolineFees, currentBlockHeight: Int): Triple { // We are either directly paying our peer (the trampoline node) or a remote node via our peer (using trampoline). val trampolineRoute = when (request.recipient) { walletParams.trampolineNode.id -> listOf( diff --git a/src/commonTest/kotlin/fr/acinq/lightning/payment/OutgoingPaymentHandlerTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/lightning/payment/OutgoingPaymentHandlerTestsCommon.kt index f634260f8..eaa4167e1 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/payment/OutgoingPaymentHandlerTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/payment/OutgoingPaymentHandlerTestsCommon.kt @@ -16,6 +16,7 @@ import fr.acinq.lightning.db.LightningOutgoingPayment import fr.acinq.lightning.db.OutgoingPaymentsDb import fr.acinq.lightning.io.PayInvoice import fr.acinq.lightning.io.WrappedChannelCommand +import fr.acinq.lightning.payment.OutgoingPaymentHandler.PaymentAttempt import fr.acinq.lightning.tests.TestConstants import fr.acinq.lightning.tests.utils.LightningTestSuite import fr.acinq.lightning.tests.utils.runSuspendTest @@ -897,6 +898,20 @@ class OutgoingPaymentHandlerTestsCommon : LightningTestSuite() { } } + @Test + fun `several large blinded paths`() = runSuspendTest { + val (alice, _) = TestsHelper.reachNormal() + val payerKey = randomKey() + val invoice = Bolt12Invoice.fromString("lni1qqs0sehhttf0swv6sxsxuefk9q23yj9h0cl4wyn324jlt2gll4fzk0syzquta4dy4m9jgp5s970w2lw8928ppnqz662mq8r6dyy7w9kgv0ann0zlk7aacwpykl7uu5adckf7t0sgpees93j6nwr4xle4ck4d2wup6j044fx6f5zkdacvjen3herm493en2lmqgprfesng92cpl68qvlxv96prguvtl7yv49nwq9a92zl5n2pv73u4wgqx08jupl9mcphw7ww5mv50j7xkcjxvjamrysy3wsfmgj4cu9aq4y6hszdk5rys4qwa4muxczk4nuj54gyw3jqygaqqk2edh9c90ergwvaxu93jynty9z0gzrpcwuhkzt05epatv45qqg93d3x5hx8jgwgkwpnz85y3narw93pqvmp27mzgwjaswd76lgxm88qw2ehqszd2mlgntvnwq0vzrk4ehq325pqdl3gcz4k7xeh9sdx5fr2uclhf7f3aqm9u9dq38rg6cvsqqqqqqq9yqjwyp2qxqsqqpvzzqnfhw87mvwxx0qrljmlxppxr7h4hmdgdzpsxnj0rw2jckjtc93g44v3gmrfva58gmnfdenjq6tnyp3k7mmvypq5dg8aqsqsxhj07sv0ez642nzanm4xvwtvyfaag2dry5wge0r3zqpt5g2mls3xqwsadj5tn7gm50l43xmsu2dszx8s672wrkqn48qp7ftw6kvhltlrkqsrwc9agnmlg8dzefxe4vlz9euhhwjzeflzrrkkurluwhzfjqf62trsq2dm4984vptwe3zpea0e3n27w75pyt2ym8g734awf99ep759a4xdf29epp5z3e303e08q2e6qdycnlzdkv4c80u2pr7ql2ndpa9zfafwyqkf9r9cqtaza35txq9rppm56ajy97nema0j8vtuld097u0hjhh5cmwhlp8du95va7wvq2emxkvafy2wg8ghl7vpkw2w0w0pp2tr6gqqmnvxy9u9jg9rkwn2flxn9d3vd9x0g94wdhgap22l780d0t9zywpj7yu949347hsag0s6pgpkuaf2pg8xqzjk336e8azw8672qtvvljye6gtevmnrygav24jvsxmypf25rqs8ersr9wfsqhdxjl6lmy3lqac4q96rjn02vtk4n6zcaqzr5qhlqlywrga7lmwt99gyj23ndzrl2y8380nxydqntsh7gpajau2wrvpshzpryq37n72ujf26djermgcf47wfsfdts3p97u0lnt6szck8zlqzqfz6ex8p9xtugj6jtuyf3flkqk9ahu8e6dxgvp49xl24x6hdmljcjqptk8erxq4uwnkv4g32p4d8un0f9az5ft59mwvgtf3xxm3xepx68x6vk46pf5xh4lyr35m32q5ukd5xj4kj7vka67uggjd4saahn3dswdwsqe8epg2pvele4cz0zvq288t94n69cj87ewal7yqvvpq6tj6mpjw2d36uhlf2khtjlpdfv672gvkszdpqht37shtf0ytyfnl7xkeudm2xscvpack3whk29hsfr36cv95ddzc2klk2yc93s09y2uh2xz5k5jap4550z2vd7gy9faxd2gcr4xjmke8yhn0wdsqxpe78qyqy0elprd0swe9yxc82as23vm5fyumc7nv470xfsjy3zjg4zfvpa6kv3ndl0w3ukc6627dua9g7pmzfupesqvpjfw3rjt39hlmk40gtralyk5a4lq42207acdqekpgmdjqpmw0zy3cz8d3cpvw4a28lqnf3etu8adl43zra0zk5m094h86g4yfm8cxnz7zqyqa5q4dz6ceqp0jeuakvyx4653h0kjllhhpfduquh4ch8rtu9lf20gqznhxe60n3m45k80vcgnvut8cfgpvy37ae93rpah9sa4ddyzyt9gu0uj5zny00vrkkxycrxm8wk3gzyrn3q8caxrqwa48fwqzlagph95y5tw555px0s97y4lgspgedkg4q9gxgv4qyc7x8433j0a5fnntdjhlsmx4wje53dj8y4c3757yt87kenvej8ugwkg05flugshd906tvtqcldrcgxfqqw8ecz926ft3ehdahpjhueag8znklv6qvz7w3htszz5k3we0dd8r2grxpz5edgtuh28u0c5ugrf8autgu3crkjt58ev9gcvnetn5kjwk4wehr8yq7lulsug7dhyg0reyqlhhsxs8ueghtm02g7jd2p9ytwstsacry3y8mv202y4qqqqqqqqqqqq9qq6qqqqqqqqqqqqqsqqqqqf2q5htpqqqqqqqraqqqqqqpqp5qqqqqqqqqqqqpqqqqqqqqh6uzcqqqqqqqqqqqqqqeqqrgqqqqqqqqqqqqzqqqqqqjnems5qqqpfqyv6g47m4gyqw3qa7penn7yv39ufk8j0mpj0ldfyu9qd7j2vcgt27rqe9l5ydpm2szfcs2uqczqqqtqggrxc2hkcjr5hvrn0kh6pkeecrjkdcyqn2kl6y6mymsrmqsa4wdcy2lqsxw9n37kdz8zq0ytckjvcy7jcwqrqj5rgd56q4e3g76k0lja96hzkdda7z5sxqwemrdjje2rqm7jcv6ll39dut0mqjvrl4skedkfz5mu").get() + val outgoingPaymentHandler = OutgoingPaymentHandler(alice.staticParams.nodeParams, defaultWalletParams, InMemoryPaymentsDb()) + val payInvoice = PayInvoice(UUID.randomUUID(), 20_000.msat, LightningOutgoingPayment.Details.Blinded(invoice, payerKey)) + val (trampolineAmount, trampolineExpiry, trampolinePacket) = outgoingPaymentHandler.createTrampolinePayload(payInvoice, defaultWalletParams.trampolineFees.first(), 851851) + val trampolinePaymentSecret = randomBytes32() + val trampolinePayload = PaymentAttempt.TrampolinePayload(trampolineAmount, trampolineExpiry, trampolinePaymentSecret, trampolinePacket) + val childPayments = outgoingPaymentHandler.createChildPayments(payInvoice, listOf(RouteCalculation.Route(24080.msat, alice.state)), trampolinePayload) + println(childPayments) + } + private fun makeInvoice(amount: MilliSatoshi?, supportsTrampoline: Boolean, privKey: PrivateKey = randomKey(), extraHops: List> = listOf()): Bolt11Invoice { val paymentPreimage: ByteVector32 = randomBytes32() val paymentHash = Crypto.sha256(paymentPreimage).toByteVector32() From bb292ad47f5f09d3398db99f573aa2f1c57048b8 Mon Sep 17 00:00:00 2001 From: Thomas HUET Date: Mon, 15 Jul 2024 11:34:04 +0200 Subject: [PATCH 2/3] Prune route hints and blinded paths for trampoline --- .../acinq/lightning/crypto/sphinx/Sphinx.kt | 2 +- .../payment/OutgoingPaymentHandler.kt | 4 +-- .../payment/OutgoingPaymentPacket.kt | 29 ++++++++++++++++--- .../OutgoingPaymentHandlerTestsCommon.kt | 15 ---------- .../payment/PaymentPacketTestsCommon.kt | 7 +++++ 5 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/commonMain/kotlin/fr/acinq/lightning/crypto/sphinx/Sphinx.kt b/src/commonMain/kotlin/fr/acinq/lightning/crypto/sphinx/Sphinx.kt index 4f0d76398..f22ab194e 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/crypto/sphinx/Sphinx.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/crypto/sphinx/Sphinx.kt @@ -214,7 +214,7 @@ object Sphinx { is Either.Right -> packet.value.payload.size() } - require(payload.size <= packetLength - MacLength) { "packet payload cannot exceed ${packetLength - MacLength} bytes" } + require(payload.size <= packetLength - MacLength) { "packet payload cannot exceed ${packetLength - MacLength} bytes, is ${payload.size} bytes" } val (currentMac, currentPayload) = when (packet) { // Packet construction starts with an empty mac and random payload. diff --git a/src/commonMain/kotlin/fr/acinq/lightning/payment/OutgoingPaymentHandler.kt b/src/commonMain/kotlin/fr/acinq/lightning/payment/OutgoingPaymentHandler.kt index c370e63a4..8c0c3d042 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/payment/OutgoingPaymentHandler.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/payment/OutgoingPaymentHandler.kt @@ -95,7 +95,7 @@ class OutgoingPaymentHandler(val nodeParams: NodeParams, val walletParams: Walle } } - fun createChildPayments(request: PayInvoice, routes: List, trampolinePayload: PaymentAttempt.TrampolinePayload): List> { + private fun createChildPayments(request: PayInvoice, routes: List, trampolinePayload: PaymentAttempt.TrampolinePayload): List> { val logger = MDCLogger(logger, staticMdc = request.mdc()) val childPayments = routes.map { createOutgoingPart(request, it, trampolinePayload) } childToParentId.putAll(childPayments.map { it.first.id to request.paymentId }) @@ -337,7 +337,7 @@ class OutgoingPaymentHandler(val nodeParams: NodeParams, val walletParams: Walle return Triple(outgoingPayment, secrets, WrappedChannelCommand(route.channel.channelId, cmdAdd)) } - fun createTrampolinePayload(request: PayInvoice, fees: TrampolineFees, currentBlockHeight: Int): Triple { + private fun createTrampolinePayload(request: PayInvoice, fees: TrampolineFees, currentBlockHeight: Int): Triple { // We are either directly paying our peer (the trampoline node) or a remote node via our peer (using trampoline). val trampolineRoute = when (request.recipient) { walletParams.trampolineNode.id -> listOf( diff --git a/src/commonMain/kotlin/fr/acinq/lightning/payment/OutgoingPaymentPacket.kt b/src/commonMain/kotlin/fr/acinq/lightning/payment/OutgoingPaymentPacket.kt index 395be443a..365dd43b5 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/payment/OutgoingPaymentPacket.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/payment/OutgoingPaymentPacket.kt @@ -18,6 +18,7 @@ import fr.acinq.lightning.router.Hop import fr.acinq.lightning.router.NodeHop import fr.acinq.lightning.utils.UUID import fr.acinq.lightning.wire.FailureMessage +import fr.acinq.lightning.wire.OnionPaymentPayloadTlv import fr.acinq.lightning.wire.OnionRoutingPacket import fr.acinq.lightning.wire.PaymentOnion @@ -72,7 +73,7 @@ object OutgoingPaymentPacket { // NB: the final payload will never reach the recipient, since the next-to-last trampoline hop will convert that to a legacy payment // We use the smallest final payload possible, otherwise we may overflow the trampoline onion size. val dummyFinalPayload = PaymentOnion.FinalPayload.Standard.createSinglePartPayload(finalPayload.amount, finalPayload.expiry, finalPayload.paymentSecret, null) - val (firstAmount, firstExpiry, payloads) = hops.drop(1).reversed().fold(Triple(finalPayload.amount, finalPayload.expiry, listOf(dummyFinalPayload))) { triple, hop -> + val (firstAmount, firstExpiry, initialPayloads) = hops.drop(1).reversed().fold(Triple(finalPayload.amount, finalPayload.expiry, listOf(dummyFinalPayload))) { triple, hop -> val (amount, expiry, payloads) = triple val payload = when (payloads.size) { // The next-to-last trampoline hop must include invoice data to indicate the conversion to a legacy payment. @@ -81,8 +82,20 @@ object OutgoingPaymentPacket { } Triple(amount + hop.fee(amount), expiry + hop.cltvExpiryDelta, listOf(payload) + payloads) } + var payloads = initialPayloads val nodes = hops.map { it.nextNodeId } - val onion = buildOnion(nodes, payloads, invoice.paymentHash, payloadLength = null) + var onion = buildOnion(nodes, payloads, invoice.paymentHash, payloadLength = null) + // Ensure that this onion can fit inside the outer 1300 bytes onion. + while (onion.packet.payload.size() > 1142) { + payloads = payloads.map { payload -> when (payload) { + is PaymentOnion.RelayToNonTrampolinePayload -> payload.copy(records = payload.records.copy(records = payload.records.records.map { when (it) { + is OnionPaymentPayloadTlv.InvoiceRoutingInfo -> OnionPaymentPayloadTlv.InvoiceRoutingInfo(it.extraHops.dropLast(1)) + else -> it + } }.toSet())) + else -> payload + } } + onion = buildOnion(nodes, payloads, invoice.paymentHash, payloadLength = null) + } return Triple(firstAmount, firstExpiry, onion) } @@ -97,8 +110,16 @@ object OutgoingPaymentPacket { * @param finalExpiry cltv expiry that should be received by the final recipient. */ fun buildTrampolineToNonTrampolinePacket(invoice: Bolt12Invoice, hop: NodeHop, finalAmount: MilliSatoshi, finalExpiry: CltvExpiry): Triple { - val payload = PaymentOnion.RelayToBlindedPayload.create(finalAmount, finalExpiry, invoice) - val onion = buildOnion(listOf(hop.nodeId), listOf(payload), invoice.paymentHash, payloadLength = null) + var payload = PaymentOnion.RelayToBlindedPayload.create(finalAmount, finalExpiry, invoice) + var onion = buildOnion(listOf(hop.nodeId), listOf(payload), invoice.paymentHash, payloadLength = null) + // Ensure that this onion can fit inside the outer 1300 bytes onion. + while (onion.packet.payload.size() > 1142) { + payload = payload.copy(records = payload.records.copy(records = payload.records.records.map { when (it) { + is OnionPaymentPayloadTlv.OutgoingBlindedPaths -> OnionPaymentPayloadTlv.OutgoingBlindedPaths(it.paths.dropLast(1)) + else -> it + } }.toSet())) + onion = buildOnion(listOf(hop.nodeId), listOf(payload), invoice.paymentHash, payloadLength = null) + } return Triple(finalAmount + hop.fee(finalAmount), finalExpiry + hop.cltvExpiryDelta, onion) } diff --git a/src/commonTest/kotlin/fr/acinq/lightning/payment/OutgoingPaymentHandlerTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/lightning/payment/OutgoingPaymentHandlerTestsCommon.kt index eaa4167e1..f634260f8 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/payment/OutgoingPaymentHandlerTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/payment/OutgoingPaymentHandlerTestsCommon.kt @@ -16,7 +16,6 @@ import fr.acinq.lightning.db.LightningOutgoingPayment import fr.acinq.lightning.db.OutgoingPaymentsDb import fr.acinq.lightning.io.PayInvoice import fr.acinq.lightning.io.WrappedChannelCommand -import fr.acinq.lightning.payment.OutgoingPaymentHandler.PaymentAttempt import fr.acinq.lightning.tests.TestConstants import fr.acinq.lightning.tests.utils.LightningTestSuite import fr.acinq.lightning.tests.utils.runSuspendTest @@ -898,20 +897,6 @@ class OutgoingPaymentHandlerTestsCommon : LightningTestSuite() { } } - @Test - fun `several large blinded paths`() = runSuspendTest { - val (alice, _) = TestsHelper.reachNormal() - val payerKey = randomKey() - val invoice = Bolt12Invoice.fromString("lni1qqs0sehhttf0swv6sxsxuefk9q23yj9h0cl4wyn324jlt2gll4fzk0syzquta4dy4m9jgp5s970w2lw8928ppnqz662mq8r6dyy7w9kgv0ann0zlk7aacwpykl7uu5adckf7t0sgpees93j6nwr4xle4ck4d2wup6j044fx6f5zkdacvjen3herm493en2lmqgprfesng92cpl68qvlxv96prguvtl7yv49nwq9a92zl5n2pv73u4wgqx08jupl9mcphw7ww5mv50j7xkcjxvjamrysy3wsfmgj4cu9aq4y6hszdk5rys4qwa4muxczk4nuj54gyw3jqygaqqk2edh9c90ergwvaxu93jynty9z0gzrpcwuhkzt05epatv45qqg93d3x5hx8jgwgkwpnz85y3narw93pqvmp27mzgwjaswd76lgxm88qw2ehqszd2mlgntvnwq0vzrk4ehq325pqdl3gcz4k7xeh9sdx5fr2uclhf7f3aqm9u9dq38rg6cvsqqqqqqq9yqjwyp2qxqsqqpvzzqnfhw87mvwxx0qrljmlxppxr7h4hmdgdzpsxnj0rw2jckjtc93g44v3gmrfva58gmnfdenjq6tnyp3k7mmvypq5dg8aqsqsxhj07sv0ez642nzanm4xvwtvyfaag2dry5wge0r3zqpt5g2mls3xqwsadj5tn7gm50l43xmsu2dszx8s672wrkqn48qp7ftw6kvhltlrkqsrwc9agnmlg8dzefxe4vlz9euhhwjzeflzrrkkurluwhzfjqf62trsq2dm4984vptwe3zpea0e3n27w75pyt2ym8g734awf99ep759a4xdf29epp5z3e303e08q2e6qdycnlzdkv4c80u2pr7ql2ndpa9zfafwyqkf9r9cqtaza35txq9rppm56ajy97nema0j8vtuld097u0hjhh5cmwhlp8du95va7wvq2emxkvafy2wg8ghl7vpkw2w0w0pp2tr6gqqmnvxy9u9jg9rkwn2flxn9d3vd9x0g94wdhgap22l780d0t9zywpj7yu949347hsag0s6pgpkuaf2pg8xqzjk336e8azw8672qtvvljye6gtevmnrygav24jvsxmypf25rqs8ersr9wfsqhdxjl6lmy3lqac4q96rjn02vtk4n6zcaqzr5qhlqlywrga7lmwt99gyj23ndzrl2y8380nxydqntsh7gpajau2wrvpshzpryq37n72ujf26djermgcf47wfsfdts3p97u0lnt6szck8zlqzqfz6ex8p9xtugj6jtuyf3flkqk9ahu8e6dxgvp49xl24x6hdmljcjqptk8erxq4uwnkv4g32p4d8un0f9az5ft59mwvgtf3xxm3xepx68x6vk46pf5xh4lyr35m32q5ukd5xj4kj7vka67uggjd4saahn3dswdwsqe8epg2pvele4cz0zvq288t94n69cj87ewal7yqvvpq6tj6mpjw2d36uhlf2khtjlpdfv672gvkszdpqht37shtf0ytyfnl7xkeudm2xscvpack3whk29hsfr36cv95ddzc2klk2yc93s09y2uh2xz5k5jap4550z2vd7gy9faxd2gcr4xjmke8yhn0wdsqxpe78qyqy0elprd0swe9yxc82as23vm5fyumc7nv470xfsjy3zjg4zfvpa6kv3ndl0w3ukc6627dua9g7pmzfupesqvpjfw3rjt39hlmk40gtralyk5a4lq42207acdqekpgmdjqpmw0zy3cz8d3cpvw4a28lqnf3etu8adl43zra0zk5m094h86g4yfm8cxnz7zqyqa5q4dz6ceqp0jeuakvyx4653h0kjllhhpfduquh4ch8rtu9lf20gqznhxe60n3m45k80vcgnvut8cfgpvy37ae93rpah9sa4ddyzyt9gu0uj5zny00vrkkxycrxm8wk3gzyrn3q8caxrqwa48fwqzlagph95y5tw555px0s97y4lgspgedkg4q9gxgv4qyc7x8433j0a5fnntdjhlsmx4wje53dj8y4c3757yt87kenvej8ugwkg05flugshd906tvtqcldrcgxfqqw8ecz926ft3ehdahpjhueag8znklv6qvz7w3htszz5k3we0dd8r2grxpz5edgtuh28u0c5ugrf8autgu3crkjt58ev9gcvnetn5kjwk4wehr8yq7lulsug7dhyg0reyqlhhsxs8ueghtm02g7jd2p9ytwstsacry3y8mv202y4qqqqqqqqqqqq9qq6qqqqqqqqqqqqqsqqqqqf2q5htpqqqqqqqraqqqqqqpqp5qqqqqqqqqqqqpqqqqqqqqh6uzcqqqqqqqqqqqqqqeqqrgqqqqqqqqqqqqzqqqqqqjnems5qqqpfqyv6g47m4gyqw3qa7penn7yv39ufk8j0mpj0ldfyu9qd7j2vcgt27rqe9l5ydpm2szfcs2uqczqqqtqggrxc2hkcjr5hvrn0kh6pkeecrjkdcyqn2kl6y6mymsrmqsa4wdcy2lqsxw9n37kdz8zq0ytckjvcy7jcwqrqj5rgd56q4e3g76k0lja96hzkdda7z5sxqwemrdjje2rqm7jcv6ll39dut0mqjvrl4skedkfz5mu").get() - val outgoingPaymentHandler = OutgoingPaymentHandler(alice.staticParams.nodeParams, defaultWalletParams, InMemoryPaymentsDb()) - val payInvoice = PayInvoice(UUID.randomUUID(), 20_000.msat, LightningOutgoingPayment.Details.Blinded(invoice, payerKey)) - val (trampolineAmount, trampolineExpiry, trampolinePacket) = outgoingPaymentHandler.createTrampolinePayload(payInvoice, defaultWalletParams.trampolineFees.first(), 851851) - val trampolinePaymentSecret = randomBytes32() - val trampolinePayload = PaymentAttempt.TrampolinePayload(trampolineAmount, trampolineExpiry, trampolinePaymentSecret, trampolinePacket) - val childPayments = outgoingPaymentHandler.createChildPayments(payInvoice, listOf(RouteCalculation.Route(24080.msat, alice.state)), trampolinePayload) - println(childPayments) - } - private fun makeInvoice(amount: MilliSatoshi?, supportsTrampoline: Boolean, privKey: PrivateKey = randomKey(), extraHops: List> = listOf()): Bolt11Invoice { val paymentPreimage: ByteVector32 = randomBytes32() val paymentHash = Crypto.sha256(paymentPreimage).toByteVector32() diff --git a/src/commonTest/kotlin/fr/acinq/lightning/payment/PaymentPacketTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/lightning/payment/PaymentPacketTestsCommon.kt index d00dd7fbc..a4527ca3a 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/payment/PaymentPacketTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/payment/PaymentPacketTestsCommon.kt @@ -657,5 +657,12 @@ class PaymentPacketTestsCommon : LightningTestSuite() { val failure = IncomingPaymentPacket.decrypt(UpdateAddHtlc(randomBytes32(), 4, amountE, paymentHash, expiryE, onionE.packet), privE) assertEquals(Either.Left(FinalIncorrectCltvExpiry(invalidExpiry)), failure) } + + @Test + fun `relay to blinded with many large blinded routes`() { + val invoice = Bolt12Invoice.fromString("lni1qqs0sehhttf0swv6sxsxuefk9q23yj9h0cl4wyn324jlt2gll4fzk0syzquta4dy4m9jgp5s970w2lw8928ppnqz662mq8r6dyy7w9kgv0ann0zlk7aacwpykl7uu5adckf7t0sgpees93j6nwr4xle4ck4d2wup6j044fx6f5zkdacvjen3herm493en2lmqgprfesng92cpl68qvlxv96prguvtl7yv49nwq9a92zl5n2pv73u4wgqx08jupl9mcphw7ww5mv50j7xkcjxvjamrysy3wsfmgj4cu9aq4y6hszdk5rys4qwa4muxczk4nuj54gyw3jqygaqqk2edh9c90ergwvaxu93jynty9z0gzrpcwuhkzt05epatv45qqg93d3x5hx8jgwgkwpnz85y3narw93pqvmp27mzgwjaswd76lgxm88qw2ehqszd2mlgntvnwq0vzrk4ehq325pqdl3gcz4k7xeh9sdx5fr2uclhf7f3aqm9u9dq38rg6cvsqqqqqqq9yqjwyp2qxqsqqpvzzqnfhw87mvwxx0qrljmlxppxr7h4hmdgdzpsxnj0rw2jckjtc93g44v3gmrfva58gmnfdenjq6tnyp3k7mmvypq5dg8aqsqsxhj07sv0ez642nzanm4xvwtvyfaag2dry5wge0r3zqpt5g2mls3xqwsadj5tn7gm50l43xmsu2dszx8s672wrkqn48qp7ftw6kvhltlrkqsrwc9agnmlg8dzefxe4vlz9euhhwjzeflzrrkkurluwhzfjqf62trsq2dm4984vptwe3zpea0e3n27w75pyt2ym8g734awf99ep759a4xdf29epp5z3e303e08q2e6qdycnlzdkv4c80u2pr7ql2ndpa9zfafwyqkf9r9cqtaza35txq9rppm56ajy97nema0j8vtuld097u0hjhh5cmwhlp8du95va7wvq2emxkvafy2wg8ghl7vpkw2w0w0pp2tr6gqqmnvxy9u9jg9rkwn2flxn9d3vd9x0g94wdhgap22l780d0t9zywpj7yu949347hsag0s6pgpkuaf2pg8xqzjk336e8azw8672qtvvljye6gtevmnrygav24jvsxmypf25rqs8ersr9wfsqhdxjl6lmy3lqac4q96rjn02vtk4n6zcaqzr5qhlqlywrga7lmwt99gyj23ndzrl2y8380nxydqntsh7gpajau2wrvpshzpryq37n72ujf26djermgcf47wfsfdts3p97u0lnt6szck8zlqzqfz6ex8p9xtugj6jtuyf3flkqk9ahu8e6dxgvp49xl24x6hdmljcjqptk8erxq4uwnkv4g32p4d8un0f9az5ft59mwvgtf3xxm3xepx68x6vk46pf5xh4lyr35m32q5ukd5xj4kj7vka67uggjd4saahn3dswdwsqe8epg2pvele4cz0zvq288t94n69cj87ewal7yqvvpq6tj6mpjw2d36uhlf2khtjlpdfv672gvkszdpqht37shtf0ytyfnl7xkeudm2xscvpack3whk29hsfr36cv95ddzc2klk2yc93s09y2uh2xz5k5jap4550z2vd7gy9faxd2gcr4xjmke8yhn0wdsqxpe78qyqy0elprd0swe9yxc82as23vm5fyumc7nv470xfsjy3zjg4zfvpa6kv3ndl0w3ukc6627dua9g7pmzfupesqvpjfw3rjt39hlmk40gtralyk5a4lq42207acdqekpgmdjqpmw0zy3cz8d3cpvw4a28lqnf3etu8adl43zra0zk5m094h86g4yfm8cxnz7zqyqa5q4dz6ceqp0jeuakvyx4653h0kjllhhpfduquh4ch8rtu9lf20gqznhxe60n3m45k80vcgnvut8cfgpvy37ae93rpah9sa4ddyzyt9gu0uj5zny00vrkkxycrxm8wk3gzyrn3q8caxrqwa48fwqzlagph95y5tw555px0s97y4lgspgedkg4q9gxgv4qyc7x8433j0a5fnntdjhlsmx4wje53dj8y4c3757yt87kenvej8ugwkg05flugshd906tvtqcldrcgxfqqw8ecz926ft3ehdahpjhueag8znklv6qvz7w3htszz5k3we0dd8r2grxpz5edgtuh28u0c5ugrf8autgu3crkjt58ev9gcvnetn5kjwk4wehr8yq7lulsug7dhyg0reyqlhhsxs8ueghtm02g7jd2p9ytwstsacry3y8mv202y4qqqqqqqqqqqq9qq6qqqqqqqqqqqqqsqqqqqf2q5htpqqqqqqqraqqqqqqpqp5qqqqqqqqqqqqpqqqqqqqqh6uzcqqqqqqqqqqqqqqeqqrgqqqqqqqqqqqqzqqqqqqjnems5qqqpfqyv6g47m4gyqw3qa7penn7yv39ufk8j0mpj0ldfyu9qd7j2vcgt27rqe9l5ydpm2szfcs2uqczqqqtqggrxc2hkcjr5hvrn0kh6pkeecrjkdcyqn2kl6y6mymsrmqsa4wdcy2lqsxw9n37kdz8zq0ytckjvcy7jcwqrqj5rgd56q4e3g76k0lja96hzkdda7z5sxqwemrdjje2rqm7jcv6ll39dut0mqjvrl4skedkfz5mu").get() + val (_, _, trampolineOnion) = OutgoingPaymentPacket.buildTrampolineToNonTrampolinePacket(invoice, NodeHop(randomKey().publicKey(), randomKey().publicKey(), CltvExpiryDelta(444), 0.msat), finalAmount, finalExpiry) + assertTrue(trampolineOnion.packet.payload.size() < 1142) + } } From 844040f1b47946303c9cb8c724c16a11680e505e Mon Sep 17 00:00:00 2001 From: Thomas HUET Date: Mon, 15 Jul 2024 14:03:06 +0200 Subject: [PATCH 3/3] 1000 --- .../fr/acinq/lightning/payment/OutgoingPaymentPacket.kt | 8 ++++---- .../acinq/lightning/payment/PaymentPacketTestsCommon.kt | 7 +++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/commonMain/kotlin/fr/acinq/lightning/payment/OutgoingPaymentPacket.kt b/src/commonMain/kotlin/fr/acinq/lightning/payment/OutgoingPaymentPacket.kt index 365dd43b5..7d0a1fc0d 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/payment/OutgoingPaymentPacket.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/payment/OutgoingPaymentPacket.kt @@ -85,8 +85,8 @@ object OutgoingPaymentPacket { var payloads = initialPayloads val nodes = hops.map { it.nextNodeId } var onion = buildOnion(nodes, payloads, invoice.paymentHash, payloadLength = null) - // Ensure that this onion can fit inside the outer 1300 bytes onion. - while (onion.packet.payload.size() > 1142) { + // Ensure that this onion can fit inside the outer 1300 bytes onion. The outer onion fields need ~150 bytes and we add some safety margin. + while (onion.packet.payload.size() > 1000) { payloads = payloads.map { payload -> when (payload) { is PaymentOnion.RelayToNonTrampolinePayload -> payload.copy(records = payload.records.copy(records = payload.records.records.map { when (it) { is OnionPaymentPayloadTlv.InvoiceRoutingInfo -> OnionPaymentPayloadTlv.InvoiceRoutingInfo(it.extraHops.dropLast(1)) @@ -112,8 +112,8 @@ object OutgoingPaymentPacket { fun buildTrampolineToNonTrampolinePacket(invoice: Bolt12Invoice, hop: NodeHop, finalAmount: MilliSatoshi, finalExpiry: CltvExpiry): Triple { var payload = PaymentOnion.RelayToBlindedPayload.create(finalAmount, finalExpiry, invoice) var onion = buildOnion(listOf(hop.nodeId), listOf(payload), invoice.paymentHash, payloadLength = null) - // Ensure that this onion can fit inside the outer 1300 bytes onion. - while (onion.packet.payload.size() > 1142) { + // Ensure that this onion can fit inside the outer 1300 bytes onion. The outer onion fields need ~150 bytes and we add some safety margin. + while (onion.packet.payload.size() > 1000) { payload = payload.copy(records = payload.records.copy(records = payload.records.records.map { when (it) { is OnionPaymentPayloadTlv.OutgoingBlindedPaths -> OnionPaymentPayloadTlv.OutgoingBlindedPaths(it.paths.dropLast(1)) else -> it diff --git a/src/commonTest/kotlin/fr/acinq/lightning/payment/PaymentPacketTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/lightning/payment/PaymentPacketTestsCommon.kt index a4527ca3a..c780d31f5 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/payment/PaymentPacketTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/payment/PaymentPacketTestsCommon.kt @@ -12,6 +12,7 @@ import fr.acinq.lightning.channel.states.Channel import fr.acinq.lightning.crypto.RouteBlinding import fr.acinq.lightning.crypto.sphinx.Sphinx import fr.acinq.lightning.crypto.sphinx.Sphinx.hash +import fr.acinq.lightning.payment.OutgoingPaymentHandler.PaymentAttempt import fr.acinq.lightning.router.ChannelHop import fr.acinq.lightning.router.NodeHop import fr.acinq.lightning.tests.utils.LightningTestSuite @@ -661,8 +662,10 @@ class PaymentPacketTestsCommon : LightningTestSuite() { @Test fun `relay to blinded with many large blinded routes`() { val invoice = Bolt12Invoice.fromString("lni1qqs0sehhttf0swv6sxsxuefk9q23yj9h0cl4wyn324jlt2gll4fzk0syzquta4dy4m9jgp5s970w2lw8928ppnqz662mq8r6dyy7w9kgv0ann0zlk7aacwpykl7uu5adckf7t0sgpees93j6nwr4xle4ck4d2wup6j044fx6f5zkdacvjen3herm493en2lmqgprfesng92cpl68qvlxv96prguvtl7yv49nwq9a92zl5n2pv73u4wgqx08jupl9mcphw7ww5mv50j7xkcjxvjamrysy3wsfmgj4cu9aq4y6hszdk5rys4qwa4muxczk4nuj54gyw3jqygaqqk2edh9c90ergwvaxu93jynty9z0gzrpcwuhkzt05epatv45qqg93d3x5hx8jgwgkwpnz85y3narw93pqvmp27mzgwjaswd76lgxm88qw2ehqszd2mlgntvnwq0vzrk4ehq325pqdl3gcz4k7xeh9sdx5fr2uclhf7f3aqm9u9dq38rg6cvsqqqqqqq9yqjwyp2qxqsqqpvzzqnfhw87mvwxx0qrljmlxppxr7h4hmdgdzpsxnj0rw2jckjtc93g44v3gmrfva58gmnfdenjq6tnyp3k7mmvypq5dg8aqsqsxhj07sv0ez642nzanm4xvwtvyfaag2dry5wge0r3zqpt5g2mls3xqwsadj5tn7gm50l43xmsu2dszx8s672wrkqn48qp7ftw6kvhltlrkqsrwc9agnmlg8dzefxe4vlz9euhhwjzeflzrrkkurluwhzfjqf62trsq2dm4984vptwe3zpea0e3n27w75pyt2ym8g734awf99ep759a4xdf29epp5z3e303e08q2e6qdycnlzdkv4c80u2pr7ql2ndpa9zfafwyqkf9r9cqtaza35txq9rppm56ajy97nema0j8vtuld097u0hjhh5cmwhlp8du95va7wvq2emxkvafy2wg8ghl7vpkw2w0w0pp2tr6gqqmnvxy9u9jg9rkwn2flxn9d3vd9x0g94wdhgap22l780d0t9zywpj7yu949347hsag0s6pgpkuaf2pg8xqzjk336e8azw8672qtvvljye6gtevmnrygav24jvsxmypf25rqs8ersr9wfsqhdxjl6lmy3lqac4q96rjn02vtk4n6zcaqzr5qhlqlywrga7lmwt99gyj23ndzrl2y8380nxydqntsh7gpajau2wrvpshzpryq37n72ujf26djermgcf47wfsfdts3p97u0lnt6szck8zlqzqfz6ex8p9xtugj6jtuyf3flkqk9ahu8e6dxgvp49xl24x6hdmljcjqptk8erxq4uwnkv4g32p4d8un0f9az5ft59mwvgtf3xxm3xepx68x6vk46pf5xh4lyr35m32q5ukd5xj4kj7vka67uggjd4saahn3dswdwsqe8epg2pvele4cz0zvq288t94n69cj87ewal7yqvvpq6tj6mpjw2d36uhlf2khtjlpdfv672gvkszdpqht37shtf0ytyfnl7xkeudm2xscvpack3whk29hsfr36cv95ddzc2klk2yc93s09y2uh2xz5k5jap4550z2vd7gy9faxd2gcr4xjmke8yhn0wdsqxpe78qyqy0elprd0swe9yxc82as23vm5fyumc7nv470xfsjy3zjg4zfvpa6kv3ndl0w3ukc6627dua9g7pmzfupesqvpjfw3rjt39hlmk40gtralyk5a4lq42207acdqekpgmdjqpmw0zy3cz8d3cpvw4a28lqnf3etu8adl43zra0zk5m094h86g4yfm8cxnz7zqyqa5q4dz6ceqp0jeuakvyx4653h0kjllhhpfduquh4ch8rtu9lf20gqznhxe60n3m45k80vcgnvut8cfgpvy37ae93rpah9sa4ddyzyt9gu0uj5zny00vrkkxycrxm8wk3gzyrn3q8caxrqwa48fwqzlagph95y5tw555px0s97y4lgspgedkg4q9gxgv4qyc7x8433j0a5fnntdjhlsmx4wje53dj8y4c3757yt87kenvej8ugwkg05flugshd906tvtqcldrcgxfqqw8ecz926ft3ehdahpjhueag8znklv6qvz7w3htszz5k3we0dd8r2grxpz5edgtuh28u0c5ugrf8autgu3crkjt58ev9gcvnetn5kjwk4wehr8yq7lulsug7dhyg0reyqlhhsxs8ueghtm02g7jd2p9ytwstsacry3y8mv202y4qqqqqqqqqqqq9qq6qqqqqqqqqqqqqsqqqqqf2q5htpqqqqqqqraqqqqqqpqp5qqqqqqqqqqqqpqqqqqqqqh6uzcqqqqqqqqqqqqqqeqqrgqqqqqqqqqqqqzqqqqqqjnems5qqqpfqyv6g47m4gyqw3qa7penn7yv39ufk8j0mpj0ldfyu9qd7j2vcgt27rqe9l5ydpm2szfcs2uqczqqqtqggrxc2hkcjr5hvrn0kh6pkeecrjkdcyqn2kl6y6mymsrmqsa4wdcy2lqsxw9n37kdz8zq0ytckjvcy7jcwqrqj5rgd56q4e3g76k0lja96hzkdda7z5sxqwemrdjje2rqm7jcv6ll39dut0mqjvrl4skedkfz5mu").get() - val (_, _, trampolineOnion) = OutgoingPaymentPacket.buildTrampolineToNonTrampolinePacket(invoice, NodeHop(randomKey().publicKey(), randomKey().publicKey(), CltvExpiryDelta(444), 0.msat), finalAmount, finalExpiry) - assertTrue(trampolineOnion.packet.payload.size() < 1142) + val (trampolineAmount, trampolineExpiry, trampolineOnion) = OutgoingPaymentPacket.buildTrampolineToNonTrampolinePacket(invoice, NodeHop(randomKey().publicKey(), randomKey().publicKey(), CltvExpiryDelta(444), 0.msat), finalAmount, finalExpiry) + val trampolinePayload = PaymentAttempt.TrampolinePayload(trampolineAmount, trampolineExpiry, randomBytes32(), trampolineOnion.packet) + val channelHops: List = listOf(ChannelHop(randomKey().publicKey(), randomKey().publicKey(), defaultChannelUpdate)) + OutgoingPaymentPacket.buildCommand(UUID.randomUUID(), randomBytes32(), channelHops, trampolinePayload.createFinalPayload(finalAmount)) } }