Skip to content
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

RPC: parse BOLT12 invoices and offers #2843

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

rorp
Copy link
Contributor

@rorp rorp commented Mar 28, 2024

This PR adds the ability to parse BOLT12 invoices to parseinvoice RPC call, and introduces parseoffer RPC call, that parses BOLT12 offers.

@codecov-commenter
Copy link

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 86.00%. Comparing base (c8184b3) to head (6d01eee).

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2843      +/-   ##
==========================================
+ Coverage   85.96%   86.00%   +0.03%     
==========================================
  Files         219      219              
  Lines       18441    18442       +1     
  Branches      762      731      -31     
==========================================
+ Hits        15853    15861       +8     
+ Misses       2588     2581       -7     
Files Coverage Δ
...n/scala/fr/acinq/eclair/json/JsonSerializers.scala 96.42% <ø> (ø)
...la/fr/acinq/eclair/payment/send/OfferPayment.scala 72.91% <100.00%> (+0.57%) ⬆️

... and 5 files with indirect coverage changes

Copy link
Member

@t-bast t-bast left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You seem to be doing more than just adding a parseoffer API in this PR, you're also adding support for Bolt 12 invoices which is arguably not a good idea...

@@ -138,6 +138,7 @@ private class OfferPayment(replyTo: ActorRef,
Behaviors.stopped
case None =>
context.spawnAnonymous(CompactBlindedPathsResolver(router)) ! Resolve(context.messageAdapter[Seq[ResolvedPath]](WrappedResolvedPaths), payload.invoice.blindedPaths)
context.log.info(s"BOLT12 invoice: ${payload.invoice}")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: remove?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really. It's really useful for debugging. How else could we know what kind of invoices our counterparties send to us?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand...? Why is it useful to log the Bolt 12 invoice we receive? If the payment completes it will be stored in our payments DB. If the payment fails, do you really need the invoice to investigate why it failed? It will generally be easy to figure out what fails without it?

Comment on lines +87 to +89
val invoiceUnmarshaller: Unmarshaller[String, Invoice] = Unmarshaller.strict { str =>
Bolt11Invoice.fromString(str).orElse(Bolt12Invoice.fromString(str)).get
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you introducing this? It creates unnecessary complexity in API handlers since we don't support directly paying a Bolt 12 invoice (we should always start with an offer and go through the invoice_request flow)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please advise how it is supposed to be done in the right way? All I need is to have an ability to parse Bolt 12 invoices and offers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To parse an invoice when you don't know which kind of invoice to expect, you should use Invoice.fromString.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant is that Bolt 12 invoices shouldn't be exposed, see #2843 (comment)

There was a decision to not even specify a string encoding for them, we have one in eclair for consistency (and because we added it before the spec removed it), but it shouldn't be relied on.

@@ -47,7 +47,7 @@ trait ExtraDirectives extends Directives {
val outPointsFormParam: NameUnmarshallerReceptacle[List[OutPoint]] = "outpoints".as[List[OutPoint]](outPointListUnmarshaller)
val paymentHashFormParam: NameUnmarshallerReceptacle[ByteVector32] = "paymentHash".as[ByteVector32](bytes32Unmarshaller)
val amountMsatFormParam: NameReceptacle[MilliSatoshi] = "amountMsat".as[MilliSatoshi]
val invoiceFormParam: NameReceptacle[Bolt11Invoice] = "invoice".as[Bolt11Invoice]
val invoiceFormParam: NameUnmarshallerReceptacle[Invoice] = "invoice".as[Invoice](invoiceUnmarshaller)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should keep this for Bolt 11 only, Bolt 12 invoices should be obtained with the invoice_request flow and never accepted as a parameter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand the question, sorry.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually not a question but a statement. I guess t-bast added a question mark to ask if you object to this statement.
Your reason to use it is parseinvoice and I agree that it would be nice to have, but it would be cleaner to keep this only for Bolt11Invoice (renaming it to bolt11invoiceFormParam) and create a new invoiceFormParam that you use only for parseinvoice.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the spec point of view, Bolt 12 invoices should not be exposed to end users, only the offer should be exposed. You shouldn't be able to explicitly pay a Bolt 12 invoice, APIs should only allow you to pay a Bolt 12 offer.

So I'm not sure why you'd want to be able to parse them, since they're meant to be internal objects that aren't exposed to end users. On top of that, they're just a TLV stream, so it won't look nice in JSON at all...

@rorp
Copy link
Contributor Author

rorp commented Mar 29, 2024

You seem to be doing more than just adding a parseoffer API in this PR, you're also adding support for Bolt 12 invoices which is arguably not a good idea...

Not at all. Only parseinvoice was changed to support Bolt 12 invoices. The changes in other RPCs were made just to prevent MatchError exceptions and avoid compiler errors.

@t-bast
Copy link
Member

t-bast commented Mar 29, 2024

Only parseinvoice was changed to support Bolt 12 invoices.

This is what threw me off, I don't think we should do this. But I may be wrong, @thomash-acinq do you think this would be useful?

@thomash-acinq
Copy link
Member

If there was an explicit decision to hide Bolt 12 invoices from the user (I didn't realize it was removed from the spec), then we shouldn't expose them.

If it's only for debugging purposes, what I usually do is run this test:

test("show invoice"){
  val encodedInvoice = "lni1qqgds4gw..."
  val invoice = Bolt12Invoice.fromString(encodedInvoice).get
  invoice.records.records.foreach(println)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants