-
Notifications
You must be signed in to change notification settings - Fork 409
Hide InvoiceFeatures behind InvoiceBuilder API #901
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
Hide InvoiceFeatures behind InvoiceBuilder API #901
Conversation
@@ -970,6 +1036,7 @@ impl Invoice { | |||
signed_invoice: signed_invoice, | |||
}; | |||
invoice.check_field_counts()?; | |||
invoice.check_feature_bits()?; |
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.
Are there cases where you may want to use this on an invoice not generated by us? Where we may want to accept a non-basic-mpp invoice but not generate them?
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.
I believe this is supported as written. check_feature_bits
simply checks that BOLT 11 requirements are met. Note that the changes in this PR don't require that any feature bits are set. I know we discussed doing that offline yesterday, but I ended up finding a simpler way to enforce the semantics.
} | ||
|
||
impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, T, C, tb::False> { | ||
/// Sets the payment secret and relevant features. |
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.
Hmm, we still need to be able to set more features here, no? You may want to set some future features which is probably in known
but not in the set that is required to understand the invoice here.
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.
Do you mean setting a feature as optional? If so, that is an open question that I had. For instance, would we want to have both basic_mpp_required
and basic_mpp_optional
methods? It wasn't entirely clear from the BOLT if either were possible:
- if the
basic_mpp
feature is offered in the invoice:
- MAY pay using Basic multi-part payments.
- otherwise:
- MUST NOT use Basic multi-part payments.
Does "offered" mean the odd bit (optional) was set? Is something similar possible for payment_secret
? The BOLT seems to imply that if a secret is set then it must be used:
- if there is a valid
s
field:
- MUST use that as
payment_secret
And what would optional mean here if basic_mpp
is set as required as there is a dependency requirement?
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.
Do you mean setting a feature as optional?
I meant a feature other than the two below. eg what if we have option_htlcs_colored_blue
? Some users may really like blue HTLCs some users may not, and LDK may reasonably support receiving both. Thus, we need some way to provide a Features
object, I think.
Does "offered" mean the odd bit (optional) was set?
I believe it means either. I think the handling of var_len_onion here is fine.
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.
I meant a feature other than the two below. eg what if we have
option_htlcs_colored_blue
? Some users may really like blue HTLCs some users may not, and LDK may reasonably support receiving both. Thus, we need some way to provide aFeatures
object, I think.
Ah, so the additional type parameter S
is for specifying that a secret can be set at most once. Additionally, it is used to condition whether basic_mpp
can bet set because that requires the payment_secret
feature and thus also a secret field set in the invoice.
Other features may have similar type parameters associated with them if they require some other fields set in the invoice. For example, if the hypothetical feature were instead option_htlcs_colored
with an additional field specifying the color blue, then we would add an option_htlcs_colored
method taking a color to set in the invoice. It would also set the feature bits as necessary.
Long story short is the purpose of InvoiceBuilder
is to enforce BOLT 11 semantics at compile time. Thus, it can never result in a SemanticsError
only a CreationError
. Allowing features to be set arbitrarily breaks that contract.
Whether the features are set as required or optional is an orthogonal concern. Since these features are optional in InvoiceFeatures::known()
, then we'd likely want to set them optional here. I think I had decided on required because the dependency between them and wasn't sure what it would mean for some to be optional and others to be required.
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.
Hmmmmm, right, for "known" bits that makes sense. I do wonder how we can support "experimental" feature bits (eg DLC invoices or so), but we really need the ability to set them in InvoiceFeatures
as well which would imply its a separate thing.
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.
For experimental features, I'd imagine there could be a separate method to set these which could also check against any known features.
Codecov Report
@@ Coverage Diff @@
## main #901 +/- ##
==========================================
+ Coverage 90.50% 90.60% +0.10%
==========================================
Files 59 59
Lines 29624 30466 +842
==========================================
+ Hits 26810 27604 +794
- Misses 2814 2862 +48
Continue to review full report at Codecov.
|
ACK modulo being able to set unknown feature bits |
I think we can do that in a followup? Currently |
Feature payment_secret is required and depends on var_onion_optin, so the latter must also be required.
2ddb995
to
61c79de
Compare
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 looking pretty reasonable to me! I'd prefer if test changes were included in the commit they're testing so it's a bit clearer what's being tested, but not a huge deal
I tend to agree though sometimes having a few atomic commits that culminate in the the tests can alleviate the review burden. Here, the tests make use of |
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.
Code Review ACK 718753d
I don't think my point around basic_mpp
matters given current spec.
self.tagged_fields = self.tagged_fields | ||
.drain(..) | ||
.map(|field| match field { | ||
TaggedField::Features(f) => TaggedField::Features(f.set_basic_mpp_optional()), |
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.
Note, what if in the future we introduce a new tagged field requiring to set the 9
field without being dependent on payment secret. We might set mpp here without actually having payment_secret
set in our feature bits ?
Maybe we should add a if features.supports_payment_secret { ... }
?
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 method is conditionally implemented when payment_secret
is set (i.e., when the S
type parameter is tb::True
). So supporting such a feature should not be problem -- calling basic_mpp()
would be a compilation error still.
Instead of relying on users to set an invoice's features correctly, enforce the semantics inside InvoiceBuilder. For instance, if the user sets a PaymentSecret then InvoiceBuilder should ensure the appropriate feature bits are set. Thus, for this example, the TaggedField abstraction can be retained while still ensuring BOLT 11 semantics at the builder abstraction.
Since InvoiceFeatures are an implementation detail of InvoiceBuilder, an explicit call is needed to support the basic_mpp feature. Since it is dependent on the payment_secret feature, conditionally define the builder's method only when payment_secret has been set.
718753d
to
2226ae2
Compare
Squashed fixup commits. |
Code Review ACK 2226ae2 |
Instead of relying on users to set an invoice's features correctly, enforce the semantics inside
InvoiceBuilder
. For instance, if the user sets aPaymentSecret
thenInvoiceBuilder
should ensure the appropriate feature bits are set. Thus, for this example, theTaggedField
abstraction can be retained while still ensuring BOLT 11 semantics at the builder abstraction.Based on #898.