@@ -12,6 +12,7 @@ import fr.acinq.bitcoin.utils.flatMap
1212import fr.acinq.lightning.*
1313import fr.acinq.lightning.payment.Bolt11Invoice
1414import fr.acinq.lightning.payment.Bolt12Invoice
15+ import fr.acinq.lightning.payment.Bolt12Invoice.Companion.PaymentBlindedContactInfo
1516import fr.acinq.lightning.utils.msat
1617import fr.acinq.lightning.utils.toByteVector
1718
@@ -151,16 +152,43 @@ sealed class OnionPaymentPayloadTlv : Tlv {
151152 }
152153
153154 /* *
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.
155+ * Features that may be used to reach the recipient, provided by the payment sender (usually obtained them from an invoice).
156+ * Only included for a trampoline node when relaying to a non- trampoline recipient using [OutgoingBlindedPaths] or [InvoiceRoutingInfo] .
156157 */
157- data class InvoiceFeatures (val features : ByteVector ) : OnionPaymentPayloadTlv() {
158- override val tag: Long get() = InvoiceFeatures .tag
158+ data class RecipientFeatures (val features : ByteVector ) : OnionPaymentPayloadTlv() {
159+ override val tag: Long get() = RecipientFeatures .tag
159160 override fun write (out : Output ) = LightningCodecs .writeBytes(features, out )
160161
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)))
162+ companion object : TlvValueReader <RecipientFeatures > {
163+ const val tag: Long = 21
164+ override fun read (input : Input ): RecipientFeatures = RecipientFeatures (ByteVector (LightningCodecs .bytes(input, input.availableBytes)))
165+ }
166+ }
167+
168+ /* *
169+ * Blinded paths that can be used to reach the final recipient.
170+ * Only included for a trampoline node when paying a Bolt 12 invoice that doesn't support trampoline.
171+ */
172+ data class OutgoingBlindedPaths (val paths : List <PaymentBlindedContactInfo >) : OnionPaymentPayloadTlv() {
173+ override val tag: Long get() = OutgoingBlindedPaths .tag
174+ override fun write (out : Output ) {
175+ for (path in paths) {
176+ OfferTypes .writePath(path.route, out )
177+ OfferTypes .writePaymentInfo(path.paymentInfo, out )
178+ }
179+ }
180+
181+ companion object : TlvValueReader <OutgoingBlindedPaths > {
182+ const val tag: Long = 22
183+ override fun read (input : Input ): OutgoingBlindedPaths {
184+ val paths = ArrayList <PaymentBlindedContactInfo >()
185+ while (input.availableBytes > 0 ) {
186+ val route = OfferTypes .readPath(input)
187+ val payInfo = OfferTypes .readPaymentInfo(input)
188+ paths.add(Bolt12Invoice .Companion .PaymentBlindedContactInfo (route, payInfo))
189+ }
190+ return OutgoingBlindedPaths (paths)
191+ }
164192 }
165193 }
166194
@@ -205,30 +233,6 @@ sealed class OnionPaymentPayloadTlv : Tlv {
205233 }
206234 }
207235
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-
232236}
233237
234238object PaymentOnion {
@@ -256,9 +260,10 @@ object PaymentOnion {
256260 OnionPaymentPayloadTlv .PaymentMetadata .tag to OnionPaymentPayloadTlv .PaymentMetadata .Companion as TlvValueReader <OnionPaymentPayloadTlv >,
257261 OnionPaymentPayloadTlv .TotalAmount .tag to OnionPaymentPayloadTlv .TotalAmount .Companion as TlvValueReader <OnionPaymentPayloadTlv >,
258262 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 >,
263+ OnionPaymentPayloadTlv .RecipientFeatures .tag to OnionPaymentPayloadTlv .RecipientFeatures .Companion as TlvValueReader <OnionPaymentPayloadTlv >,
261264 OnionPaymentPayloadTlv .OutgoingBlindedPaths .tag to OnionPaymentPayloadTlv .OutgoingBlindedPaths .Companion as TlvValueReader <OnionPaymentPayloadTlv >,
265+ // The following TLVs aren't official TLVs from the BOLTs.
266+ OnionPaymentPayloadTlv .InvoiceRoutingInfo .tag to OnionPaymentPayloadTlv .InvoiceRoutingInfo .Companion as TlvValueReader <OnionPaymentPayloadTlv >,
262267 )
263268 )
264269
@@ -423,6 +428,32 @@ object PaymentOnion {
423428 }
424429 }
425430
431+ data class BlindedChannelRelayPayload (val records : TlvStream <OnionPaymentPayloadTlv >) : PerHopPayload() {
432+ override fun write (out : Output ) = tlvSerializer.write(records, out )
433+
434+ companion object : PerHopPayloadReader <BlindedChannelRelayPayload > {
435+ override fun read (input : Input ): Either <InvalidOnionPayload , BlindedChannelRelayPayload > {
436+ return PerHopPayload .read(input).flatMap { tlvs ->
437+ when {
438+ tlvs.get<OnionPaymentPayloadTlv .AmountToForward >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .AmountToForward .tag, 0 ))
439+ tlvs.get<OnionPaymentPayloadTlv .OutgoingCltv >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .OutgoingCltv .tag, 0 ))
440+ tlvs.get<OnionPaymentPayloadTlv .OutgoingChannelId >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .OutgoingChannelId .tag, 0 ))
441+ tlvs.get<OnionPaymentPayloadTlv .EncryptedRecipientData >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .EncryptedRecipientData .tag, 0 ))
442+ else -> Either .Right (BlindedChannelRelayPayload (tlvs))
443+ }
444+ }
445+ }
446+
447+ fun create (encryptedData : ByteVector , blinding : PublicKey ? ): BlindedChannelRelayPayload {
448+ val tlvs = buildSet {
449+ add(OnionPaymentPayloadTlv .EncryptedRecipientData (encryptedData))
450+ blinding?.let { add(OnionPaymentPayloadTlv .BlindingPoint (it)) }
451+ }
452+ return BlindedChannelRelayPayload (TlvStream (tlvs))
453+ }
454+ }
455+ }
456+
426457 data class NodeRelayPayload (val records : TlvStream <OnionPaymentPayloadTlv >) : PerHopPayload() {
427458 val amountToForward = records.get<OnionPaymentPayloadTlv .AmountToForward >()!! .amount
428459 val outgoingCltv = records.get<OnionPaymentPayloadTlv .OutgoingCltv >()!! .cltv
@@ -468,7 +499,7 @@ object PaymentOnion {
468499 val outgoingNodeId = records.get<OnionPaymentPayloadTlv .OutgoingNodeId >()!! .nodeId
469500 val paymentSecret = records.get<OnionPaymentPayloadTlv .PaymentData >()!! .secret
470501 val paymentMetadata = records.get<OnionPaymentPayloadTlv .PaymentMetadata >()?.data
471- val invoiceFeatures = records.get<OnionPaymentPayloadTlv .InvoiceFeatures >()!! .features
502+ val invoiceFeatures = records.get<OnionPaymentPayloadTlv .RecipientFeatures >()!! .features
472503 val invoiceRoutingInfo = records.get<OnionPaymentPayloadTlv .InvoiceRoutingInfo >()!! .extraHops
473504
474505 override fun write (out : Output ) = tlvSerializer.write(records, out )
@@ -481,7 +512,7 @@ object PaymentOnion {
481512 tlvs.get<OnionPaymentPayloadTlv .OutgoingCltv >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .OutgoingCltv .tag, 0 ))
482513 tlvs.get<OnionPaymentPayloadTlv .OutgoingNodeId >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .OutgoingNodeId .tag, 0 ))
483514 tlvs.get<OnionPaymentPayloadTlv .PaymentData >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .PaymentData .tag, 0 ))
484- tlvs.get<OnionPaymentPayloadTlv .InvoiceFeatures >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .InvoiceFeatures .tag, 0 ))
515+ tlvs.get<OnionPaymentPayloadTlv .RecipientFeatures >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .RecipientFeatures .tag, 0 ))
485516 tlvs.get<OnionPaymentPayloadTlv .InvoiceRoutingInfo >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .InvoiceRoutingInfo .tag, 0 ))
486517 tlvs.get<OnionPaymentPayloadTlv .EncryptedRecipientData >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .EncryptedRecipientData .tag, 0 ))
487518 tlvs.get<OnionPaymentPayloadTlv .BlindingPoint >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .BlindingPoint .tag, 0 ))
@@ -499,19 +530,23 @@ object PaymentOnion {
499530 add(OnionPaymentPayloadTlv .OutgoingNodeId (targetNodeId))
500531 add(OnionPaymentPayloadTlv .PaymentData (invoice.paymentSecret, totalAmount))
501532 invoice.paymentMetadata?.let { add(OnionPaymentPayloadTlv .PaymentMetadata (it)) }
502- add(OnionPaymentPayloadTlv .InvoiceFeatures (invoice.features.toByteArray().toByteVector()))
533+ add(OnionPaymentPayloadTlv .RecipientFeatures (invoice.features.toByteArray().toByteVector()))
503534 add(OnionPaymentPayloadTlv .InvoiceRoutingInfo (routingInfo.map { it.hints }))
504535 }
505536 )
506537 )
507538 }
508539 }
509540
541+ /* *
542+ * Create a trampoline payload to tell our trampoline node to relay to a blinded path, where the recipient doesn't support trampoline.
543+ * This only reveals the blinded path to our trampoline node, which doesn't reveal the recipient's identity.
544+ */
510545 data class RelayToBlindedPayload (val records : TlvStream <OnionPaymentPayloadTlv >) : PerHopPayload() {
511546 val amountToForward = records.get<OnionPaymentPayloadTlv .AmountToForward >()!! .amount
512547 val outgoingCltv = records.get<OnionPaymentPayloadTlv .OutgoingCltv >()!! .cltv
513548 val outgoingBlindedPaths = records.get<OnionPaymentPayloadTlv .OutgoingBlindedPaths >()!! .paths
514- val invoiceFeatures = records.get<OnionPaymentPayloadTlv .InvoiceFeatures >()!! .features
549+ val recipientFeatures = records.get<OnionPaymentPayloadTlv .RecipientFeatures >()? .features ? : Features .empty
515550
516551 override fun write (out : Output ) = tlvSerializer.write(records, out )
517552
@@ -521,7 +556,6 @@ object PaymentOnion {
521556 when {
522557 tlvs.get<OnionPaymentPayloadTlv .AmountToForward >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .AmountToForward .tag, 0 ))
523558 tlvs.get<OnionPaymentPayloadTlv .OutgoingCltv >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .OutgoingCltv .tag, 0 ))
524- tlvs.get<OnionPaymentPayloadTlv .InvoiceFeatures >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .InvoiceFeatures .tag, 0 ))
525559 tlvs.get<OnionPaymentPayloadTlv .OutgoingBlindedPaths >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .OutgoingBlindedPaths .tag, 0 ))
526560 tlvs.get<OnionPaymentPayloadTlv .EncryptedRecipientData >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .EncryptedRecipientData .tag, 0 ))
527561 tlvs.get<OnionPaymentPayloadTlv .BlindingPoint >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .BlindingPoint .tag, 0 ))
@@ -530,14 +564,14 @@ object PaymentOnion {
530564 }
531565 }
532566
533- fun create (amount : MilliSatoshi , expiry : CltvExpiry , features : Features , blindedPaths : List <Bolt12Invoice . Companion . PaymentBlindedContactInfo >): RelayToBlindedPayload =
567+ fun create (amount : MilliSatoshi , expiry : CltvExpiry , features : Features , blindedPaths : List <PaymentBlindedContactInfo >): RelayToBlindedPayload =
534568 RelayToBlindedPayload (
535569 TlvStream (
536570 setOf (
537571 OnionPaymentPayloadTlv .AmountToForward (amount),
538572 OnionPaymentPayloadTlv .OutgoingCltv (expiry),
539573 OnionPaymentPayloadTlv .OutgoingBlindedPaths (blindedPaths),
540- OnionPaymentPayloadTlv .InvoiceFeatures (features.toByteArray().toByteVector())
574+ OnionPaymentPayloadTlv .RecipientFeatures (features.toByteArray().toByteVector())
541575 )
542576 )
543577 )
0 commit comments