@@ -11,7 +11,7 @@ import fr.acinq.bitcoin.utils.Either
1111import fr.acinq.bitcoin.utils.flatMap
1212import fr.acinq.lightning.*
1313import fr.acinq.lightning.payment.Bolt11Invoice
14- import fr.acinq.lightning.payment.Bolt12Invoice
14+ import fr.acinq.lightning.payment.Bolt12Invoice.Companion.PaymentBlindedContactInfo
1515import fr.acinq.lightning.utils.msat
1616import fr.acinq.lightning.utils.toByteVector
1717
@@ -151,16 +151,43 @@ sealed class OnionPaymentPayloadTlv : Tlv {
151151 }
152152
153153 /* *
154- * Invoice feature bits. Only included for intermediate trampoline nodes when they should convert to a legacy payment
155- * because the final recipient doesn't support trampoline.
154+ * Features that may be used to reach the recipient, provided by the payment sender (usually obtained them from an invoice).
155+ * Only included for a trampoline node when relaying to a non- trampoline recipient using [OutgoingBlindedPaths] or [InvoiceRoutingInfo] .
156156 */
157- data class InvoiceFeatures (val features : ByteVector ) : OnionPaymentPayloadTlv() {
158- override val tag: Long get() = InvoiceFeatures .tag
157+ data class RecipientFeatures (val features : ByteVector ) : OnionPaymentPayloadTlv() {
158+ override val tag: Long get() = RecipientFeatures .tag
159159 override fun write (out : Output ) = LightningCodecs .writeBytes(features, out )
160160
161- companion object : TlvValueReader <InvoiceFeatures > {
162- const val tag: Long = 66097
163- override fun read (input : Input ): InvoiceFeatures = InvoiceFeatures (ByteVector (LightningCodecs .bytes(input, input.availableBytes)))
161+ companion object : TlvValueReader <RecipientFeatures > {
162+ const val tag: Long = 21
163+ override fun read (input : Input ): RecipientFeatures = RecipientFeatures (ByteVector (LightningCodecs .bytes(input, input.availableBytes)))
164+ }
165+ }
166+
167+ /* *
168+ * Blinded paths that can be used to reach the final recipient.
169+ * Only included for a trampoline node when paying a Bolt 12 invoice that doesn't support trampoline.
170+ */
171+ data class OutgoingBlindedPaths (val paths : List <PaymentBlindedContactInfo >) : OnionPaymentPayloadTlv() {
172+ override val tag: Long get() = OutgoingBlindedPaths .tag
173+ override fun write (out : Output ) {
174+ for (path in paths) {
175+ OfferTypes .writePath(path.route, out )
176+ OfferTypes .writePaymentInfo(path.paymentInfo, out )
177+ }
178+ }
179+
180+ companion object : TlvValueReader <OutgoingBlindedPaths > {
181+ const val tag: Long = 22
182+ override fun read (input : Input ): OutgoingBlindedPaths {
183+ val paths = ArrayList <PaymentBlindedContactInfo >()
184+ while (input.availableBytes > 0 ) {
185+ val route = OfferTypes .readPath(input)
186+ val payInfo = OfferTypes .readPaymentInfo(input)
187+ paths.add(PaymentBlindedContactInfo (route, payInfo))
188+ }
189+ return OutgoingBlindedPaths (paths)
190+ }
164191 }
165192 }
166193
@@ -205,30 +232,6 @@ sealed class OnionPaymentPayloadTlv : Tlv {
205232 }
206233 }
207234
208- /* * Blinded paths to relay the payment to */
209- data class OutgoingBlindedPaths (val paths : List <Bolt12Invoice .Companion .PaymentBlindedContactInfo >) : OnionPaymentPayloadTlv() {
210- override val tag: Long get() = OutgoingBlindedPaths .tag
211- override fun write (out : Output ) {
212- for (path in paths) {
213- OfferTypes .writePath(path.route, out )
214- OfferTypes .writePaymentInfo(path.paymentInfo, out )
215- }
216- }
217-
218- companion object : TlvValueReader <OutgoingBlindedPaths > {
219- const val tag: Long = 66102
220- override fun read (input : Input ): OutgoingBlindedPaths {
221- val paths = ArrayList <Bolt12Invoice .Companion .PaymentBlindedContactInfo >()
222- while (input.availableBytes > 0 ) {
223- val route = OfferTypes .readPath(input)
224- val payInfo = OfferTypes .readPaymentInfo(input)
225- paths.add(Bolt12Invoice .Companion .PaymentBlindedContactInfo (route, payInfo))
226- }
227- return OutgoingBlindedPaths (paths)
228- }
229- }
230- }
231-
232235}
233236
234237object PaymentOnion {
@@ -256,9 +259,10 @@ object PaymentOnion {
256259 OnionPaymentPayloadTlv .PaymentMetadata .tag to OnionPaymentPayloadTlv .PaymentMetadata .Companion as TlvValueReader <OnionPaymentPayloadTlv >,
257260 OnionPaymentPayloadTlv .TotalAmount .tag to OnionPaymentPayloadTlv .TotalAmount .Companion as TlvValueReader <OnionPaymentPayloadTlv >,
258261 OnionPaymentPayloadTlv .TrampolineOnion .tag to OnionPaymentPayloadTlv .TrampolineOnion .Companion as TlvValueReader <OnionPaymentPayloadTlv >,
259- OnionPaymentPayloadTlv .InvoiceFeatures .tag to OnionPaymentPayloadTlv .InvoiceFeatures .Companion as TlvValueReader <OnionPaymentPayloadTlv >,
260- OnionPaymentPayloadTlv .InvoiceRoutingInfo .tag to OnionPaymentPayloadTlv .InvoiceRoutingInfo .Companion as TlvValueReader <OnionPaymentPayloadTlv >,
262+ OnionPaymentPayloadTlv .RecipientFeatures .tag to OnionPaymentPayloadTlv .RecipientFeatures .Companion as TlvValueReader <OnionPaymentPayloadTlv >,
261263 OnionPaymentPayloadTlv .OutgoingBlindedPaths .tag to OnionPaymentPayloadTlv .OutgoingBlindedPaths .Companion as TlvValueReader <OnionPaymentPayloadTlv >,
264+ // The following TLVs aren't official TLVs from the BOLTs.
265+ OnionPaymentPayloadTlv .InvoiceRoutingInfo .tag to OnionPaymentPayloadTlv .InvoiceRoutingInfo .Companion as TlvValueReader <OnionPaymentPayloadTlv >,
262266 )
263267 )
264268
@@ -401,6 +405,7 @@ object PaymentOnion {
401405 data class ChannelRelayPayload (val records : TlvStream <OnionPaymentPayloadTlv >) : PerHopPayload() {
402406 val amountToForward = records.get<OnionPaymentPayloadTlv .AmountToForward >()!! .amount
403407 val outgoingCltv = records.get<OnionPaymentPayloadTlv .OutgoingCltv >()!! .cltv
408+ val outgoingChannelId = records.get<OnionPaymentPayloadTlv .OutgoingChannelId >()!! .shortChannelId
404409
405410 override fun write (out : Output ) = tlvSerializer.write(records, out )
406411
@@ -427,7 +432,23 @@ object PaymentOnion {
427432
428433 companion object : PerHopPayloadReader <BlindedChannelRelayPayload > {
429434 override fun read (input : Input ): Either <InvalidOnionPayload , BlindedChannelRelayPayload > {
430- return PerHopPayload .read(input).map { BlindedChannelRelayPayload (it) }
435+ return PerHopPayload .read(input).flatMap { tlvs ->
436+ when {
437+ tlvs.get<OnionPaymentPayloadTlv .AmountToForward >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .AmountToForward .tag, 0 ))
438+ tlvs.get<OnionPaymentPayloadTlv .OutgoingCltv >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .OutgoingCltv .tag, 0 ))
439+ tlvs.get<OnionPaymentPayloadTlv .OutgoingChannelId >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .OutgoingChannelId .tag, 0 ))
440+ tlvs.get<OnionPaymentPayloadTlv .EncryptedRecipientData >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .EncryptedRecipientData .tag, 0 ))
441+ else -> Either .Right (BlindedChannelRelayPayload (tlvs))
442+ }
443+ }
444+ }
445+
446+ fun create (encryptedData : ByteVector , pathKey : PublicKey ? ): BlindedChannelRelayPayload {
447+ val tlvs = buildSet {
448+ add(OnionPaymentPayloadTlv .EncryptedRecipientData (encryptedData))
449+ pathKey?.let { add(OnionPaymentPayloadTlv .PathKey (it)) }
450+ }
451+ return BlindedChannelRelayPayload (TlvStream (tlvs))
431452 }
432453 }
433454 }
@@ -477,7 +498,7 @@ object PaymentOnion {
477498 val outgoingNodeId = records.get<OnionPaymentPayloadTlv .OutgoingNodeId >()!! .nodeId
478499 val paymentSecret = records.get<OnionPaymentPayloadTlv .PaymentData >()!! .secret
479500 val paymentMetadata = records.get<OnionPaymentPayloadTlv .PaymentMetadata >()?.data
480- val invoiceFeatures = records.get<OnionPaymentPayloadTlv .InvoiceFeatures >()!! .features
501+ val invoiceFeatures = records.get<OnionPaymentPayloadTlv .RecipientFeatures >()!! .features
481502 val invoiceRoutingInfo = records.get<OnionPaymentPayloadTlv .InvoiceRoutingInfo >()!! .extraHops
482503
483504 override fun write (out : Output ) = tlvSerializer.write(records, out )
@@ -490,7 +511,7 @@ object PaymentOnion {
490511 tlvs.get<OnionPaymentPayloadTlv .OutgoingCltv >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .OutgoingCltv .tag, 0 ))
491512 tlvs.get<OnionPaymentPayloadTlv .OutgoingNodeId >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .OutgoingNodeId .tag, 0 ))
492513 tlvs.get<OnionPaymentPayloadTlv .PaymentData >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .PaymentData .tag, 0 ))
493- tlvs.get<OnionPaymentPayloadTlv .InvoiceFeatures >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .InvoiceFeatures .tag, 0 ))
514+ tlvs.get<OnionPaymentPayloadTlv .RecipientFeatures >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .RecipientFeatures .tag, 0 ))
494515 tlvs.get<OnionPaymentPayloadTlv .InvoiceRoutingInfo >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .InvoiceRoutingInfo .tag, 0 ))
495516 tlvs.get<OnionPaymentPayloadTlv .EncryptedRecipientData >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .EncryptedRecipientData .tag, 0 ))
496517 tlvs.get<OnionPaymentPayloadTlv .PathKey >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .PathKey .tag, 0 ))
@@ -508,19 +529,23 @@ object PaymentOnion {
508529 add(OnionPaymentPayloadTlv .OutgoingNodeId (targetNodeId))
509530 add(OnionPaymentPayloadTlv .PaymentData (invoice.paymentSecret, totalAmount))
510531 invoice.paymentMetadata?.let { add(OnionPaymentPayloadTlv .PaymentMetadata (it)) }
511- add(OnionPaymentPayloadTlv .InvoiceFeatures (invoice.features.toByteArray().toByteVector()))
532+ add(OnionPaymentPayloadTlv .RecipientFeatures (invoice.features.toByteArray().toByteVector()))
512533 add(OnionPaymentPayloadTlv .InvoiceRoutingInfo (routingInfo.map { it.hints }))
513534 }
514535 )
515536 )
516537 }
517538 }
518539
540+ /* *
541+ * Create a trampoline payload to tell our trampoline node to relay to a blinded path, where the recipient doesn't support trampoline.
542+ * This only reveals the blinded path to our trampoline node, which doesn't reveal the recipient's identity.
543+ */
519544 data class RelayToBlindedPayload (val records : TlvStream <OnionPaymentPayloadTlv >) : PerHopPayload() {
520545 val amountToForward = records.get<OnionPaymentPayloadTlv .AmountToForward >()!! .amount
521546 val outgoingCltv = records.get<OnionPaymentPayloadTlv .OutgoingCltv >()!! .cltv
522547 val outgoingBlindedPaths = records.get<OnionPaymentPayloadTlv .OutgoingBlindedPaths >()!! .paths
523- val invoiceFeatures = records.get<OnionPaymentPayloadTlv .InvoiceFeatures >()!! .features
548+ val recipientFeatures = records.get<OnionPaymentPayloadTlv .RecipientFeatures >()? .features ? : Features .empty
524549
525550 override fun write (out : Output ) = tlvSerializer.write(records, out )
526551
@@ -530,7 +555,6 @@ object PaymentOnion {
530555 when {
531556 tlvs.get<OnionPaymentPayloadTlv .AmountToForward >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .AmountToForward .tag, 0 ))
532557 tlvs.get<OnionPaymentPayloadTlv .OutgoingCltv >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .OutgoingCltv .tag, 0 ))
533- tlvs.get<OnionPaymentPayloadTlv .InvoiceFeatures >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .InvoiceFeatures .tag, 0 ))
534558 tlvs.get<OnionPaymentPayloadTlv .OutgoingBlindedPaths >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .OutgoingBlindedPaths .tag, 0 ))
535559 tlvs.get<OnionPaymentPayloadTlv .EncryptedRecipientData >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .EncryptedRecipientData .tag, 0 ))
536560 tlvs.get<OnionPaymentPayloadTlv .PathKey >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .PathKey .tag, 0 ))
@@ -539,14 +563,14 @@ object PaymentOnion {
539563 }
540564 }
541565
542- fun create (amount : MilliSatoshi , expiry : CltvExpiry , features : Features , blindedPaths : List <Bolt12Invoice . Companion . PaymentBlindedContactInfo >): RelayToBlindedPayload =
566+ fun create (amount : MilliSatoshi , expiry : CltvExpiry , features : Features , blindedPaths : List <PaymentBlindedContactInfo >): RelayToBlindedPayload =
543567 RelayToBlindedPayload (
544568 TlvStream (
545569 setOf (
546570 OnionPaymentPayloadTlv .AmountToForward (amount),
547571 OnionPaymentPayloadTlv .OutgoingCltv (expiry),
548572 OnionPaymentPayloadTlv .OutgoingBlindedPaths (blindedPaths),
549- OnionPaymentPayloadTlv .InvoiceFeatures (features.toByteArray().toByteVector())
573+ OnionPaymentPayloadTlv .RecipientFeatures (features.toByteArray().toByteVector())
550574 )
551575 )
552576 )
0 commit comments