-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle feature-less BOLT 11 invoices #687
Conversation
We were not handling correctly feature-less invoices, which are spec-compliant. We do reject invoices that don't have `var_onion_optin`, but the check happens later. Instead, we currently throw a `NullPointerException`. The way we are checking requirements in the `init` block requires lazy-init properties, otherwise the object initialization will fail before any logic can happen. Also, as per the spec, the feature tag must be skipped if there are no features enabled.
|
||
val fallbackAddress: String? = tags.find { it is TaggedField.FallbackAddress }?.run { (this as TaggedField.FallbackAddress).toAddress(prefix) } | ||
|
||
override val features: Features = tags.find { it is TaggedField.Features }.run { Features((this as TaggedField.Features).bits) } | ||
override val features: Features get() = tags.filterIsInstance<TaggedField.Features>().firstOrNull()?.run { Features(this.bits) } ?: Features.empty |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the main fix.
@@ -55,7 +55,7 @@ data class Bolt11Invoice( | |||
require(description != null || descriptionHash != null) { "there must be exactly one description tag or one description hash tag" } | |||
} | |||
|
|||
override fun isExpired(currentTimestampSeconds: Long): Boolean = when (expirySeconds) { | |||
override fun isExpired(currentTimestampSeconds: Long): Boolean = when (val expirySeconds = expirySeconds) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately we can't smart cast lazy properties.
data5.addAll(encoded) | ||
} | ||
tags | ||
.filterNot { it is TaggedField.Features && it.bits.isEmpty() } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skip the features tag entirely when they are no enabled features.
@@ -498,10 +499,12 @@ class Bolt11InvoiceTestsCommon : LightningTestSuite() { | |||
assertEquals(pr1.paymentSecret, pr.paymentSecret) | |||
|
|||
// An invoice without the payment secret feature should be rejected | |||
assertTrue(Bolt11Invoice.read("lnbc40n1pw9qjvwpp5qq3w2ln6krepcslqszkrsfzwy49y0407hvks30ec6pu9s07jur3sdpstfshq5n9v9jzucm0d5s8vmm5v5s8qmmnwssyj3p6yqenwdencqzysxqrrss7ju0s4dwx6w8a95a9p2xc5vudl09gjl0w2n02sjrvffde632nxwh2l4w35nqepj4j5njhh4z65wyfc724yj6dn9wajvajfn5j7em6wsq2elakl").isFailure) | |||
assertIs<Try.Failure<Throwable>>((Bolt11Invoice.read("lnbc40n1pw9qjvwpp5qq3w2ln6krepcslqszkrsfzwy49y0407hvks30ec6pu9s07jur3sdpstfshq5n9v9jzucm0d5s8vmm5v5s8qmmnwssyj3p6yqenwdencqzysxqrrss7ju0s4dwx6w8a95a9p2xc5vudl09gjl0w2n02sjrvffde632nxwh2l4w35nqepj4j5njhh4z65wyfc724yj6dn9wajvajfn5j7em6wsq2elakl"))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test and the following one were actually throwing a NullPointerException
.
We were not handling correctly feature-less invoices, which are spec-compliant. We do reject invoices that don't have
var_onion_optin
, but the check happens later. Instead, we currently throw aNullPointerException
.The way we are checking requirements in the
init
block requires lazy-init properties, otherwise the object initialization will fail before any logic can happen.Also, as per the spec, the feature tag must be skipped at serialization if there are no features enabled.