Skip to content

Commit 3e8b0dc

Browse files
committed
Add support for official trampoline payments to blinded paths
We update the blinded path TLVs to use the official spec values. We also implement the spec test vector.
1 parent 1488c65 commit 3e8b0dc

File tree

6 files changed

+248
-51
lines changed

6 files changed

+248
-51
lines changed

src/commonMain/kotlin/fr/acinq/lightning/wire/PaymentOnion.kt

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -154,16 +154,43 @@ sealed class OnionPaymentPayloadTlv : Tlv {
154154
}
155155

156156
/**
157-
* Invoice feature bits. Only included for intermediate trampoline nodes when they should convert to a legacy payment
158-
* because the final recipient doesn't support trampoline.
157+
* Features that may be used to reach the recipient, provided by the payment sender (usually obtained them from an invoice).
158+
* Only included for a trampoline node when relaying to a non-trampoline recipient using [OutgoingBlindedPaths] or [InvoiceRoutingInfo].
159159
*/
160-
data class InvoiceFeatures(val features: ByteVector) : OnionPaymentPayloadTlv() {
161-
override val tag: Long get() = InvoiceFeatures.tag
160+
data class RecipientFeatures(val features: ByteVector) : OnionPaymentPayloadTlv() {
161+
override val tag: Long get() = RecipientFeatures.tag
162162
override fun write(out: Output) = LightningCodecs.writeBytes(features, out)
163163

164-
companion object : TlvValueReader<InvoiceFeatures> {
165-
const val tag: Long = 66097
166-
override fun read(input: Input): InvoiceFeatures = InvoiceFeatures(ByteVector(LightningCodecs.bytes(input, input.availableBytes)))
164+
companion object : TlvValueReader<RecipientFeatures> {
165+
const val tag: Long = 21
166+
override fun read(input: Input): RecipientFeatures = RecipientFeatures(ByteVector(LightningCodecs.bytes(input, input.availableBytes)))
167+
}
168+
}
169+
170+
/**
171+
* Blinded paths that can be used to reach the final recipient.
172+
* Only included for a trampoline node when paying a Bolt 12 invoice.
173+
*/
174+
data class OutgoingBlindedPaths(val paths: List<Bolt12Invoice.Companion.PaymentBlindedContactInfo>) : OnionPaymentPayloadTlv() {
175+
override val tag: Long get() = OutgoingBlindedPaths.tag
176+
override fun write(out: Output) {
177+
for (path in paths) {
178+
OfferTypes.writePath(path.route, out)
179+
OfferTypes.writePaymentInfo(path.paymentInfo, out)
180+
}
181+
}
182+
183+
companion object : TlvValueReader<OutgoingBlindedPaths> {
184+
const val tag: Long = 22
185+
override fun read(input: Input): OutgoingBlindedPaths {
186+
val paths = ArrayList<Bolt12Invoice.Companion.PaymentBlindedContactInfo>()
187+
while (input.availableBytes > 0) {
188+
val route = OfferTypes.readPath(input)
189+
val payInfo = OfferTypes.readPaymentInfo(input)
190+
paths.add(Bolt12Invoice.Companion.PaymentBlindedContactInfo(route, payInfo))
191+
}
192+
return OutgoingBlindedPaths(paths)
193+
}
167194
}
168195
}
169196

@@ -208,30 +235,6 @@ sealed class OnionPaymentPayloadTlv : Tlv {
208235
}
209236
}
210237

211-
/** Blinded paths to relay the payment to */
212-
data class OutgoingBlindedPaths(val paths: List<Bolt12Invoice.Companion.PaymentBlindedContactInfo>) : OnionPaymentPayloadTlv() {
213-
override val tag: Long get() = OutgoingBlindedPaths.tag
214-
override fun write(out: Output) {
215-
for (path in paths) {
216-
OfferTypes.writePath(path.route, out)
217-
OfferTypes.writePaymentInfo(path.paymentInfo, out)
218-
}
219-
}
220-
221-
companion object : TlvValueReader<OutgoingBlindedPaths> {
222-
const val tag: Long = 66102
223-
override fun read(input: Input): OutgoingBlindedPaths {
224-
val paths = ArrayList<Bolt12Invoice.Companion.PaymentBlindedContactInfo>()
225-
while (input.availableBytes > 0) {
226-
val route = OfferTypes.readPath(input)
227-
val payInfo = OfferTypes.readPaymentInfo(input)
228-
paths.add(Bolt12Invoice.Companion.PaymentBlindedContactInfo(route, payInfo))
229-
}
230-
return OutgoingBlindedPaths(paths)
231-
}
232-
}
233-
}
234-
235238
}
236239

237240
object PaymentOnion {
@@ -259,9 +262,9 @@ object PaymentOnion {
259262
OnionPaymentPayloadTlv.PaymentMetadata.tag to OnionPaymentPayloadTlv.PaymentMetadata.Companion as TlvValueReader<OnionPaymentPayloadTlv>,
260263
OnionPaymentPayloadTlv.TotalAmount.tag to OnionPaymentPayloadTlv.TotalAmount.Companion as TlvValueReader<OnionPaymentPayloadTlv>,
261264
OnionPaymentPayloadTlv.TrampolineOnion.tag to OnionPaymentPayloadTlv.TrampolineOnion.Companion as TlvValueReader<OnionPaymentPayloadTlv>,
262-
OnionPaymentPayloadTlv.InvoiceFeatures.tag to OnionPaymentPayloadTlv.InvoiceFeatures.Companion as TlvValueReader<OnionPaymentPayloadTlv>,
263-
OnionPaymentPayloadTlv.InvoiceRoutingInfo.tag to OnionPaymentPayloadTlv.InvoiceRoutingInfo.Companion as TlvValueReader<OnionPaymentPayloadTlv>,
265+
OnionPaymentPayloadTlv.RecipientFeatures.tag to OnionPaymentPayloadTlv.RecipientFeatures.Companion as TlvValueReader<OnionPaymentPayloadTlv>,
264266
OnionPaymentPayloadTlv.OutgoingBlindedPaths.tag to OnionPaymentPayloadTlv.OutgoingBlindedPaths.Companion as TlvValueReader<OnionPaymentPayloadTlv>,
267+
OnionPaymentPayloadTlv.InvoiceRoutingInfo.tag to OnionPaymentPayloadTlv.InvoiceRoutingInfo.Companion as TlvValueReader<OnionPaymentPayloadTlv>,
265268
)
266269
)
267270

@@ -476,7 +479,7 @@ object PaymentOnion {
476479
// NB: the following fields are only included in the trampoline-to-legacy case.
477480
val paymentSecret = records.get<OnionPaymentPayloadTlv.PaymentData>()!!.secret
478481
val paymentMetadata = records.get<OnionPaymentPayloadTlv.PaymentMetadata>()?.data
479-
val invoiceFeatures = records.get<OnionPaymentPayloadTlv.InvoiceFeatures>()!!.features
482+
val recipientFeatures = records.get<OnionPaymentPayloadTlv.RecipientFeatures>()!!.features
480483
val invoiceRoutingInfo = records.get<OnionPaymentPayloadTlv.InvoiceRoutingInfo>()!!.extraHops
481484

482485
override fun write(out: Output) = tlvSerializer.write(records, out)
@@ -489,7 +492,7 @@ object PaymentOnion {
489492
tlvs.get<OnionPaymentPayloadTlv.OutgoingCltv>() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.OutgoingCltv.tag, 0))
490493
tlvs.get<OnionPaymentPayloadTlv.OutgoingNodeId>() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.OutgoingNodeId.tag, 0))
491494
tlvs.get<OnionPaymentPayloadTlv.PaymentData>() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.PaymentData.tag, 0))
492-
tlvs.get<OnionPaymentPayloadTlv.InvoiceFeatures>() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.InvoiceFeatures.tag, 0))
495+
tlvs.get<OnionPaymentPayloadTlv.RecipientFeatures>() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.RecipientFeatures.tag, 0))
493496
tlvs.get<OnionPaymentPayloadTlv.InvoiceRoutingInfo>() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.InvoiceRoutingInfo.tag, 0))
494497
tlvs.get<OnionPaymentPayloadTlv.EncryptedRecipientData>() != null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.EncryptedRecipientData.tag, 0))
495498
tlvs.get<OnionPaymentPayloadTlv.BlindingPoint>() != null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.BlindingPoint.tag, 0))
@@ -507,7 +510,7 @@ object PaymentOnion {
507510
add(OnionPaymentPayloadTlv.OutgoingNodeId(targetNodeId))
508511
add(OnionPaymentPayloadTlv.PaymentData(invoice.paymentSecret, totalAmount))
509512
invoice.paymentMetadata?.let { add(OnionPaymentPayloadTlv.PaymentMetadata(it)) }
510-
add(OnionPaymentPayloadTlv.InvoiceFeatures(invoice.features.toByteArray().toByteVector()))
513+
add(OnionPaymentPayloadTlv.RecipientFeatures(invoice.features.toByteArray().toByteVector()))
511514
add(OnionPaymentPayloadTlv.InvoiceRoutingInfo(invoice.routingInfo.map { it.hints }))
512515
}
513516
)
@@ -519,7 +522,7 @@ object PaymentOnion {
519522
val amountToForward = records.get<OnionPaymentPayloadTlv.AmountToForward>()!!.amount
520523
val outgoingCltv = records.get<OnionPaymentPayloadTlv.OutgoingCltv>()!!.cltv
521524
val outgoingBlindedPaths = records.get<OnionPaymentPayloadTlv.OutgoingBlindedPaths>()!!.paths
522-
val invoiceFeatures = records.get<OnionPaymentPayloadTlv.InvoiceFeatures>()!!.features
525+
val recipientFeatures = records.get<OnionPaymentPayloadTlv.RecipientFeatures>()!!.features
523526

524527
override fun write(out: Output) = tlvSerializer.write(records, out)
525528

@@ -529,7 +532,7 @@ object PaymentOnion {
529532
when {
530533
tlvs.get<OnionPaymentPayloadTlv.AmountToForward>() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.AmountToForward.tag, 0))
531534
tlvs.get<OnionPaymentPayloadTlv.OutgoingCltv>() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.OutgoingCltv.tag, 0))
532-
tlvs.get<OnionPaymentPayloadTlv.InvoiceFeatures>() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.InvoiceFeatures.tag, 0))
535+
tlvs.get<OnionPaymentPayloadTlv.RecipientFeatures>() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.RecipientFeatures.tag, 0))
533536
tlvs.get<OnionPaymentPayloadTlv.OutgoingBlindedPaths>() == null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.OutgoingBlindedPaths.tag, 0))
534537
tlvs.get<OnionPaymentPayloadTlv.EncryptedRecipientData>() != null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.EncryptedRecipientData.tag, 0))
535538
tlvs.get<OnionPaymentPayloadTlv.BlindingPoint>() != null -> Either.Left(InvalidOnionPayload(OnionPaymentPayloadTlv.BlindingPoint.tag, 0))
@@ -545,7 +548,7 @@ object PaymentOnion {
545548
OnionPaymentPayloadTlv.AmountToForward(amount),
546549
OnionPaymentPayloadTlv.OutgoingCltv(expiry),
547550
OnionPaymentPayloadTlv.OutgoingBlindedPaths(invoice.blindedPaths),
548-
OnionPaymentPayloadTlv.InvoiceFeatures(invoice.features.toByteArray().toByteVector())
551+
OnionPaymentPayloadTlv.RecipientFeatures(invoice.features.toByteArray().toByteVector())
549552
)
550553
)
551554
)

0 commit comments

Comments
 (0)