Skip to content

Decode BOLT12 invoices in the DecodeInvoice API#215

Open
vincenzopalazzo wants to merge 1 commit into
lightningdevkit:mainfrom
vincenzopalazzo:claude/stupefied-mendeleev-50a203
Open

Decode BOLT12 invoices in the DecodeInvoice API#215
vincenzopalazzo wants to merge 1 commit into
lightningdevkit:mainfrom
vincenzopalazzo:claude/stupefied-mendeleev-50a203

Conversation

@vincenzopalazzo
Copy link
Copy Markdown
Contributor

Summary

  • DecodeInvoice now also decodes a hex-encoded BOLT12 invoice, not just a BOLT11 invoice string.
  • DecodeInvoiceResponse gains a kind field ("bolt11" or "bolt12") identifying which invoice type was decoded.
  • A BOLT12 invoice has no human-readable string form (it is exchanged as raw bytes over onion messages), so the input is expected to be hex-encoded and is parsed via LDK's Bolt12Invoice::try_from. Per the intentionally minimal scope, only kind is populated for a BOLT12 invoice; the remaining fields continue to apply to BOLT11 invoices.
  • CLI / MCP / client docs updated to reflect the broadened input.

Test plan

  • cargo test — new unit tests in decode_invoice.rs cover the BOLT12 invoice round-trip and rejection of unparseable / non-invoice input
  • e2e test_cli_decode_invoice and test_mcp_live_tool_calls assert kind == "bolt11"
  • cargo check --all-features --all-targets, cargo fmt --all, clippy clean for the changed code
  • Protobuf regenerated via RUSTFLAGS="--cfg genproto" cargo build -p ldk-server-grpc

AI tools: implemented with Claude Code (Claude Opus 4.7).

The `DecodeInvoice` RPC previously accepted only a BOLT11 invoice
string. It now also accepts a hex-encoded BOLT12 invoice, and the
response carries a new `kind` field ("bolt11" or "bolt12") identifying
which was decoded.

Unlike offers and BOLT11 invoices, a BOLT12 invoice has no
human-readable string encoding -- it is exchanged as raw bytes over
onion messages -- so the input is expected to be hex-encoded, and LDK's
`Bolt12Invoice` is parsed via `TryFrom<Vec<u8>>` accordingly. Per the
minimal scope here, only `kind` is populated for a BOLT12 invoice; the
remaining fields continue to apply to BOLT11 invoices.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ldk-reviews-bot
Copy link
Copy Markdown

ldk-reviews-bot commented May 20, 2026

I've assigned @benthecarman as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@benthecarman
Copy link
Copy Markdown
Collaborator

Do we even have a way to get a bolt 12 invoice?

@vincenzopalazzo
Copy link
Copy Markdown
Contributor Author

Do we even have a way to get a bolt 12 invoice?

Ah good point, and yes and no! With the new version of ldk-node we can perform a proof of payment (first version of it) with lightningdevkit/ldk-node#733 but people may want to use ldk to decode an bolt12 invoice that they are getting from somewhere else

fn decode_bolt12_invoice(invoice: &str) -> Option<DecodeInvoiceResponse> {
let bytes = Vec::<u8>::from_hex(invoice).ok()?;
Bolt12Invoice::try_from(bytes).ok()?;
Some(DecodeInvoiceResponse { kind: INVOICE_KIND_BOLT12.to_string(), ..Default::default() })
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

whats the point of decoding the bolt12 invoice if we don't fill in any data? We just validate its a bolt12 invoice and thats it?

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.

3 participants