@@ -11,7 +11,7 @@ import fr.acinq.bitcoin.utils.Either
11
11
import fr.acinq.bitcoin.utils.flatMap
12
12
import fr.acinq.lightning.*
13
13
import fr.acinq.lightning.payment.Bolt11Invoice
14
- import fr.acinq.lightning.payment.Bolt12Invoice
14
+ import fr.acinq.lightning.payment.Bolt12Invoice.Companion.PaymentBlindedContactInfo
15
15
import fr.acinq.lightning.utils.msat
16
16
import fr.acinq.lightning.utils.toByteVector
17
17
@@ -151,16 +151,43 @@ sealed class OnionPaymentPayloadTlv : Tlv {
151
151
}
152
152
153
153
/* *
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] .
156
156
*/
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
159
159
override fun write (out : Output ) = LightningCodecs .writeBytes(features, out )
160
160
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
+ }
164
191
}
165
192
}
166
193
@@ -205,30 +232,6 @@ sealed class OnionPaymentPayloadTlv : Tlv {
205
232
}
206
233
}
207
234
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
-
232
235
}
233
236
234
237
object PaymentOnion {
@@ -256,9 +259,10 @@ object PaymentOnion {
256
259
OnionPaymentPayloadTlv .PaymentMetadata .tag to OnionPaymentPayloadTlv .PaymentMetadata .Companion as TlvValueReader <OnionPaymentPayloadTlv >,
257
260
OnionPaymentPayloadTlv .TotalAmount .tag to OnionPaymentPayloadTlv .TotalAmount .Companion as TlvValueReader <OnionPaymentPayloadTlv >,
258
261
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 >,
261
263
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 >,
262
266
)
263
267
)
264
268
@@ -401,6 +405,7 @@ object PaymentOnion {
401
405
data class ChannelRelayPayload (val records : TlvStream <OnionPaymentPayloadTlv >) : PerHopPayload() {
402
406
val amountToForward = records.get<OnionPaymentPayloadTlv .AmountToForward >()!! .amount
403
407
val outgoingCltv = records.get<OnionPaymentPayloadTlv .OutgoingCltv >()!! .cltv
408
+ val outgoingChannelId = records.get<OnionPaymentPayloadTlv .OutgoingChannelId >()!! .shortChannelId
404
409
405
410
override fun write (out : Output ) = tlvSerializer.write(records, out )
406
411
@@ -427,7 +432,23 @@ object PaymentOnion {
427
432
428
433
companion object : PerHopPayloadReader <BlindedChannelRelayPayload > {
429
434
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))
431
452
}
432
453
}
433
454
}
@@ -477,7 +498,7 @@ object PaymentOnion {
477
498
val outgoingNodeId = records.get<OnionPaymentPayloadTlv .OutgoingNodeId >()!! .nodeId
478
499
val paymentSecret = records.get<OnionPaymentPayloadTlv .PaymentData >()!! .secret
479
500
val paymentMetadata = records.get<OnionPaymentPayloadTlv .PaymentMetadata >()?.data
480
- val invoiceFeatures = records.get<OnionPaymentPayloadTlv .InvoiceFeatures >()!! .features
501
+ val invoiceFeatures = records.get<OnionPaymentPayloadTlv .RecipientFeatures >()!! .features
481
502
val invoiceRoutingInfo = records.get<OnionPaymentPayloadTlv .InvoiceRoutingInfo >()!! .extraHops
482
503
483
504
override fun write (out : Output ) = tlvSerializer.write(records, out )
@@ -490,7 +511,7 @@ object PaymentOnion {
490
511
tlvs.get<OnionPaymentPayloadTlv .OutgoingCltv >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .OutgoingCltv .tag, 0 ))
491
512
tlvs.get<OnionPaymentPayloadTlv .OutgoingNodeId >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .OutgoingNodeId .tag, 0 ))
492
513
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 ))
494
515
tlvs.get<OnionPaymentPayloadTlv .InvoiceRoutingInfo >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .InvoiceRoutingInfo .tag, 0 ))
495
516
tlvs.get<OnionPaymentPayloadTlv .EncryptedRecipientData >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .EncryptedRecipientData .tag, 0 ))
496
517
tlvs.get<OnionPaymentPayloadTlv .PathKey >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .PathKey .tag, 0 ))
@@ -508,19 +529,23 @@ object PaymentOnion {
508
529
add(OnionPaymentPayloadTlv .OutgoingNodeId (targetNodeId))
509
530
add(OnionPaymentPayloadTlv .PaymentData (invoice.paymentSecret, totalAmount))
510
531
invoice.paymentMetadata?.let { add(OnionPaymentPayloadTlv .PaymentMetadata (it)) }
511
- add(OnionPaymentPayloadTlv .InvoiceFeatures (invoice.features.toByteArray().toByteVector()))
532
+ add(OnionPaymentPayloadTlv .RecipientFeatures (invoice.features.toByteArray().toByteVector()))
512
533
add(OnionPaymentPayloadTlv .InvoiceRoutingInfo (routingInfo.map { it.hints }))
513
534
}
514
535
)
515
536
)
516
537
}
517
538
}
518
539
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
+ */
519
544
data class RelayToBlindedPayload (val records : TlvStream <OnionPaymentPayloadTlv >) : PerHopPayload() {
520
545
val amountToForward = records.get<OnionPaymentPayloadTlv .AmountToForward >()!! .amount
521
546
val outgoingCltv = records.get<OnionPaymentPayloadTlv .OutgoingCltv >()!! .cltv
522
547
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
524
549
525
550
override fun write (out : Output ) = tlvSerializer.write(records, out )
526
551
@@ -530,7 +555,6 @@ object PaymentOnion {
530
555
when {
531
556
tlvs.get<OnionPaymentPayloadTlv .AmountToForward >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .AmountToForward .tag, 0 ))
532
557
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 ))
534
558
tlvs.get<OnionPaymentPayloadTlv .OutgoingBlindedPaths >() == null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .OutgoingBlindedPaths .tag, 0 ))
535
559
tlvs.get<OnionPaymentPayloadTlv .EncryptedRecipientData >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .EncryptedRecipientData .tag, 0 ))
536
560
tlvs.get<OnionPaymentPayloadTlv .PathKey >() != null -> Either .Left (InvalidOnionPayload (OnionPaymentPayloadTlv .PathKey .tag, 0 ))
@@ -539,14 +563,14 @@ object PaymentOnion {
539
563
}
540
564
}
541
565
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 =
543
567
RelayToBlindedPayload (
544
568
TlvStream (
545
569
setOf (
546
570
OnionPaymentPayloadTlv .AmountToForward (amount),
547
571
OnionPaymentPayloadTlv .OutgoingCltv (expiry),
548
572
OnionPaymentPayloadTlv .OutgoingBlindedPaths (blindedPaths),
549
- OnionPaymentPayloadTlv .InvoiceFeatures (features.toByteArray().toByteVector())
573
+ OnionPaymentPayloadTlv .RecipientFeatures (features.toByteArray().toByteVector())
550
574
)
551
575
)
552
576
)
0 commit comments