Skip to content

bolt12: add InvoiceRequest codec and structural validators#10832

Merged
ziggie1984 merged 6 commits into
lightningnetwork:masterfrom
bitromortac:2604-bolt12-1b
Jun 30, 2026
Merged

bolt12: add InvoiceRequest codec and structural validators#10832
ziggie1984 merged 6 commits into
lightningnetwork:masterfrom
bitromortac:2604-bolt12-1b

Conversation

@bitromortac

@bitromortac bitromortac commented May 22, 2026

Copy link
Copy Markdown
Collaborator

Based on #10789 (last five commits), part of #10736.

Adds the BOLT 12 InvoiceRequest message struct with PureTLVMessage-based Encode/Decode and the structural ValidateInvoiceRequestRead/Write validators. Signature verification and offer cross-validation are deferred to the Invoice and bolt12handler layers respectively.

I added a commit to address the discussion around the SubscribeOnionMessages rpc. There were other requests concerning the lnwire onion message implementation, which I haven't addressed yet. I'll open an extra PR for that.

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces the foundational codec and validation logic for BOLT 12 InvoiceRequest messages. It establishes a new bolt12 package dedicated to the encoding, decoding, and structural validation of offers and invoice requests. Furthermore, it refactors the existing BlindedPath implementation to support the introduction node variants required by the specification, ensuring that both Pubkey and Sciddir forms are correctly handled throughout the codebase, including RPC surfaces.

Highlights

  • InvoiceRequest Implementation: Added the InvoiceRequest struct along with its associated TLV-based encoding and decoding logic.
  • Structural Validation: Implemented comprehensive structural validators for both Offers and InvoiceRequests to ensure spec compliance.
  • BlindedPath Refactoring: Refactored BlindedPath to support IntroductionNode variants, specifically adding support for both Pubkey and Sciddir forms.
  • RPC and Wire Updates: Updated lnrpc and lnwire packages to accommodate the new BlindedPath structure and ensure consistent handling across the daemon.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request implements the core BOLT 12 Offer and Invoice Request functionality, introducing the bolt12 package for TLV encoding, decoding, and extensive validation. It also refactors lnwire to include a shared BlindedPath implementation supporting both pubkey and sciddir introduction nodes, while updating the RPC and onion messaging layers to utilize these new types. Review feedback focused on improving documentation and comment maintainability, specifically recommending the removal of fragile line number references, fixing a broken documentation block in the validation logic, and ensuring the release notes accurately reflect the inclusion of the invoice_request codec.

Comment thread bolt12/validate.go Outdated
Comment thread bolt12/validate.go Outdated
Comment thread docs/release-notes/release-notes-0.22.0.md
@github-actions github-actions Bot added the severity-critical Requires expert review - security/consensus critical label May 22, 2026
@github-actions

Copy link
Copy Markdown

🔴 PR Severity: CRITICAL

Automated classification | 32 files total (22 non-excluded) | 2,252 non-excluded lines changed

🔴 Critical (8 files)
  • lnwire/blinded_path.go — Lightning wire protocol: new blinded path type definition
  • lnwire/bounds.go — Lightning wire protocol: new TLV bounds declarations
  • lnwire/custom_records.go — Lightning wire protocol: custom records update
  • lnwire/intro_node.go — Lightning wire protocol: new intro node definition
  • lnwire/onion_msg_payload.go — Lightning wire protocol: onion message payload refactor
  • lnwire/pure_tlv.go — Lightning wire protocol: TLV serialization changes
  • lnwire/test_utils.go — Lightning wire protocol: test utilities in critical package
  • rpcserver.go — Core RPC server coordination
🟠 High (1 file)
  • routing/route/blindedroute.go — Payment routing: blinded route update
🟡 Medium (12 files)
  • bolt12/decode.go — New bolt12 package: TLV decode
  • bolt12/doc.go — New bolt12 package: package documentation
  • bolt12/invoice_request.go — New bolt12 package: invoice request codec
  • bolt12/offer.go — New bolt12 package: offer type
  • bolt12/pure_tlv.go — New bolt12 package: TLV primitives
  • bolt12/subtypes.go — New bolt12 package: subtype definitions
  • bolt12/tlv_types.go — New bolt12 package: TLV type constants
  • bolt12/validate.go — New bolt12 package: structural validators
  • go.mod — Dependency update
  • lnrpc/lightning.proto — RPC API definition changes
  • lnrpc/lightning.swagger.json — API swagger changes
  • onionmessage/onion_endpoint.go — Onion message endpoint update
🟢 Low (1 file)
  • docs/release-notes/release-notes-0.22.0.md — Release notes
Excluded from counting (10 files)
  • bolt12/helpers_test.go, bolt12/invoice_request_test.go, bolt12/offer_test.go, bolt12/subtypes_test.go, bolt12/validate_test.go — test files
  • lnwire/blinded_path_test.go, lnwire/custom_records_test.go, lnwire/onion_msg_payload_test.go, lnwire/pure_tlv_test.go — test files
  • lnrpc/lightning.pb.go — auto-generated protobuf

Analysis

This PR introduces BOLT 12 invoice request codec and structural validators, touching the lnwire package (Lightning wire protocol messages) extensively with 7 new/modified files covering blinded paths, onion message payloads, TLV serialization, and related types. Changes to rpcserver.go further anchor this at CRITICAL. Two severity-bump conditions are also satisfied: 22 non-excluded files (>20 threshold) and 2,252 non-excluded lines changed (>500 threshold), though the base classification is already CRITICAL from the lnwire and rpcserver.go changes.

The new bolt12/ package itself would be MEDIUM in isolation, but the deep integration with lnwire wire protocol message structures and core RPC server requires expert review of encoding correctness, TLV type assignments, and compatibility with the existing Lightning protocol implementation.


To override, add a severity-override-{critical,high,medium,low} label.
<!-- pr-severity-bot -->

@saubyk saubyk added the bolt12 label May 22, 2026
@saubyk saubyk added this to the v0.22.0 milestone May 22, 2026
@saubyk saubyk added this to lnd v0.22 May 22, 2026
@github-project-automation github-project-automation Bot moved this to Backlog in lnd v0.22 May 22, 2026
@saubyk saubyk moved this from Backlog to In progress in lnd v0.22 May 22, 2026
@bitromortac bitromortac marked this pull request as ready for review June 11, 2026 13:31
@bitromortac

Copy link
Copy Markdown
Collaborator Author

This is ready for review. @Abdulkbk and @vctt94, thank you for your reviews on the other PR. A review here would be greatly appreciated here as well, if you find the time.

@erickcestari erickcestari left a comment

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.

Some tests are missing to achieve greater coverage, but it's looking good!

go test ./bolt12/ -coverprofile=/tmp/cover.out && \
        go tool cover -func=/tmp/cover.out
ok      github.com/lightningnetwork/lnd/bolt12  (cached)        coverage: 92.1% of statements
github.com/lightningnetwork/lnd/bolt12/decode.go:13:            decodeStream                    75.0%
github.com/lightningnetwork/lnd/bolt12/invoice_request.go:100:  AllRecords                      100.0%
github.com/lightningnetwork/lnd/bolt12/invoice_request.go:109:  allRecordProducers              100.0%
github.com/lightningnetwork/lnd/bolt12/invoice_request.go:139:  Encode                          66.7%
github.com/lightningnetwork/lnd/bolt12/invoice_request.go:155:  DecodeInvoiceRequest            97.9%
github.com/lightningnetwork/lnd/bolt12/invoice_request.go:234:  NewInvoiceRequestFromOffer      100.0%
github.com/lightningnetwork/lnd/bolt12/offer.go:72:             AllRecords                      100.0%
github.com/lightningnetwork/lnd/bolt12/offer.go:80:             allRecordProducers              100.0%
github.com/lightningnetwork/lnd/bolt12/offer.go:99:             Encode                          66.7%
github.com/lightningnetwork/lnd/bolt12/offer.go:117:            decodeOffer                     96.4%
github.com/lightningnetwork/lnd/bolt12/pure_tlv.go:14:          bolt12InUnsignedRange           100.0%
github.com/lightningnetwork/lnd/bolt12/pure_tlv.go:23:          allRecordsFromTypeMap           100.0%
github.com/lightningnetwork/lnd/bolt12/pure_tlv.go:44:          sortedTypes                     100.0%
github.com/lightningnetwork/lnd/bolt12/subtypes.go:33:          Record                          50.0%
github.com/lightningnetwork/lnd/bolt12/subtypes.go:46:          encodeChainsRecord              71.4%
github.com/lightningnetwork/lnd/bolt12/subtypes.go:62:          decodeChainsRecord              84.6%
github.com/lightningnetwork/lnd/bolt12/tlv_types.go:14:         Record                          100.0%
github.com/lightningnetwork/lnd/bolt12/validate.go:151:         isKnownInvreqTLVType            60.0%
github.com/lightningnetwork/lnd/bolt12/validate.go:170:         ValidateInvoiceRequestWrite     86.0%
github.com/lightningnetwork/lnd/bolt12/validate.go:347:         invreqAllowedRange              100.0%
github.com/lightningnetwork/lnd/bolt12/validate.go:359:         checkInvreqAmountMeetsOffer     100.0%
github.com/lightningnetwork/lnd/bolt12/validate.go:403:         getInvreqChain                  100.0%
github.com/lightningnetwork/lnd/bolt12/validate.go:424:         ValidateInvoiceRequestRead      75.8%
github.com/lightningnetwork/lnd/bolt12/validate.go:565:         getInvoiceRequestOfferChains    100.0%
github.com/lightningnetwork/lnd/bolt12/validate.go:580:         checkInvreqQuantity             100.0%
github.com/lightningnetwork/lnd/bolt12/validate.go:629:         checkBip353Name                 100.0%
github.com/lightningnetwork/lnd/bolt12/validate.go:682:         checkBip353Alphabet             100.0%
github.com/lightningnetwork/lnd/bolt12/validate.go:700:         offerAllowedRange               100.0%
github.com/lightningnetwork/lnd/bolt12/validate.go:707:         isKnownOfferTLVType             66.7%
github.com/lightningnetwork/lnd/bolt12/validate.go:722:         ValidateOfferRead               97.7%
github.com/lightningnetwork/lnd/bolt12/validate.go:833:         getOfferChains                  100.0%
github.com/lightningnetwork/lnd/bolt12/validate.go:848:         ValidateOfferWrite              93.3%
github.com/lightningnetwork/lnd/bolt12/validate.go:933:         checkISO4217                    75.0%
github.com/lightningnetwork/lnd/bolt12/validate.go:944:         checkFeatures                   100.0%
github.com/lightningnetwork/lnd/bolt12/validate.go:970:         checkBlindedPaths               85.7%
github.com/lightningnetwork/lnd/bolt12/validate.go:996:         checkPubKeyNotNil               100.0%
github.com/lightningnetwork/lnd/bolt12/validate.go:1009:        checkAmountPositive             100.0%
github.com/lightningnetwork/lnd/bolt12/validate.go:1022:        checkUTF8                       100.0%
total:                                                          (statements)                    92.1%

Comment thread lnrpc/lightning.proto
// reply_path is the blinded path that should be used when replying to a
// received message.
/*
reply_path is the blinded path that should be used when replying to a

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.

Since we'll change the reply_path to might be a public key or a sciddir, we should also update the reply_path description in .proto.

https://github.com/bitromortac/lnd/blob/d3a1cc6d509f1b7ab806e715eca4b89a1601cd33/lnrpc/lightning.proto#L3777

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I thought about that too, but didn't do it intentionally, as we usually set the introduction_node to a pubkey and it would add more confusion than utility imo. I think keeping the doc change on the OnionMessageUpdate` is enough to document the special behavior

Comment thread bolt12/validate.go
Comment thread bolt12/validate.go Outdated
Comment thread bolt12/validate.go Outdated

@vctt94 vctt94 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the update!

I agree with Erick’s requested changes around:

  • checking present-but-nil pubkeys inside InvreqPayerID / OfferIssuerID, since IsSome() alone can still allow an encoder panic. I also reproduced the InvreqPayerID case locally with a small ValidateInvoiceRequestWrite table row using InvreqPayerID = Some(nil), which currently demonstrates the issue: ad5cb9d

  • skipping the whole BOLT 12 unsigned/signature TLV range instead of only type 240 during the invoice_request range scans.

A couple of extra notes from my pass:

  1. The SubscribeOnionMessages reply_path update looks good to me. It addresses the prior #10789 discussion by documenting that introduction_node is passed through verbatim and may be either a 33-byte pubkey or a 9-byte sciddir, and by keeping a nil reply path nil in the RPC response.

  2. The #10789 onion-message issues around final-hop namespace multiplicity and unknown even final-hop TLVs still look intentionally deferred, matching the PR description, so I did not treat them as blockers for this PR.

@ziggie1984 ziggie1984 left a comment

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.

Great work again !!!

I don't have much here just some clarifying questions.

Thank you for working on this.

Comment thread bolt12/invoice_request.go
Comment thread bolt12/invoice_request_test.go
Comment thread bolt12/invoice_request.go
Comment thread bolt12/invoice_request_test.go
Comment thread bolt12/validate.go Outdated
Comment thread bolt12/validate.go Outdated
Comment thread bolt12/validate.go Outdated
Comment thread bolt12/validate.go
Comment thread bolt12/validate.go
Comment on lines +254 to +258
// signature is intentionally omitted from this list: the spec's
// unsigned offerless variant conflicts with the unconditional
// reader signature check, and other implementations require a
// signature on every invoice_request. We always sign, so a
// present signature here is expected.

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.

is this worth reiterating on the spec level. So the read side requires a signature, whereas the InvoiceRequest for a non-offer does not ?

@bitromortac bitromortac Jun 24, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

right, I checked other implementations and I think eclair and cln require sigs as well, so probably the spec should be updated (have taken note of that)

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.

please create an issue on the BOLT repo so that it is not forgotten

@Abdulkbk Abdulkbk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Solid PR, had a few pieces of feedback.

Comment thread bolt12/validate.go
Comment thread bolt12/validate.go
Comment thread bolt12/validate.go
Comment thread bolt12/validate.go Outdated
Comment thread bolt12/validate.go
Document that the introduction_node field in an OnionMessageUpdate's
reply_path is passed through verbatim from the wire, potentially
carrying either a 33-byte pubkey or a 9-byte sciddir form. Subscribers
wishing to reply must resolve sciddir forms against their local channel
graph.

The SubscribeOnionMessages bridge is refactored to use a new
marshallBlindedPath helper, ensuring a nil reply path remains nil in the
RPC response rather than being emitted as an empty struct.
@github-actions github-actions Bot added severity-critical Requires expert review - security/consensus critical and removed severity-critical Requires expert review - security/consensus critical labels Jun 24, 2026
@bitromortac

bitromortac commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks all for your reviews! Rebased this on master (previous commit was d3a1cc6).

Edit: will fix the linter problems with the next round.

@erickcestari erickcestari left a comment

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.

LGTM! 📨

Nice work !!!

@vctt94 vctt94 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

Great job on this PR!

The InvoiceRequest is the BOLT 12 message that links a payer to an
offer: it mirrors the offer's fields so the issuer can stay stateless,
and adds the payer-specific fields and Schnorr signature that prove the
request.

It implements lnwire.PureTLVMessage so it round-trips through the shared
TLV codec.
@ziggie1984

Copy link
Copy Markdown
Collaborator

/gateway review

@lightninglabs-gateway

lightninglabs-gateway Bot commented Jun 26, 2026

Copy link
Copy Markdown

✅ Review posted: #10832 (review)

3 finding(s); 3 inline, 0 in body.

🔁 Need a re-review after pushing changes? Reply with /gateway re-review.
Maintainers can also /gateway dismiss <id> to silence specific findings, or anyone can /gateway explain <id> for elaboration.

@lightninglabs-gateway lightninglabs-gateway Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This PR adds the BOLT 12 invoice_request message struct, PureTLVMessage-based Encode/DecodeInvoiceRequest, and the structural ValidateInvoiceRequestRead/Write validators, deliberately deferring Schnorr-signature verification and offer cross-validation to later layers. The scoping is clear and the spec-to-code mapping is faithful — the read/write asymmetry on spontaneous-request fields, the unsigned-range handling, and the invreq_chain default-to-mainnet rule all match the BOLT 12 text and are well documented in comments. Test coverage of the new validators is thorough (sentinel table tests, amount-overflow, chain, BIP-353 layout).

The remaining concerns are minor API/behavior sharp edges rather than correctness defects: a constructor that can silently build an un-encodable request, a read-path validator that doesn't mirror the write-path nil-pubkey guard, and an observable RPC output change in SubscribeOnionMessages worth a release note.

Findings: 🔴 0 Blocker · 🟠 0 Major · 🟡 3 Minor · 🔵 0 Nit

Comment thread bolt12/invoice_request.go Outdated

if len(metadata) > 0 {
ir.InvreqMetadata = tlv.SomeRecordT(
tlv.RecordT[tlv.TlvType0, tlv.Blob]{

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Minor F1: Constructor can build a request that fails its own Encode()

NewInvoiceRequestFromOffer sets InvreqMetadata only when len(metadata) > 0 and InvreqPayerID only when payerID != nil, but ValidateInvoiceRequestWrite (invoked by Encode) requires both for an offer-response (ErrMissingMetadata, ErrMissingPayerID). Passing a nil payerID or empty metadata therefore returns a struct that constructs cleanly but can never encode, with the failure surfacing far from the construction site. The docstring documents neither precondition. Consider returning (*InvoiceRequest, error) and rejecting empty/nil inputs at the boundary, or documenting that both are mandatory — so "forgot to supply metadata/payer key" fails at the call site, not at a later Encode().

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.

let's do that, I think it makes sense to catch this in the contructor.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Agree, thanks also for owning the review bot's comments 🙏

Comment thread bolt12/validate.go
Comment thread rpcserver.go
@lightninglabs-gateway

lightninglabs-gateway Bot commented Jun 26, 2026

Copy link
Copy Markdown

🤖 gateway audit metadata for this PR — auto-generated, please don't edit.

@ziggie1984 ziggie1984 left a comment

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.

LGTM, had some nits

why are you not fixing the linter here tho?

Comment thread bolt12/validate_test.go Outdated
Comment thread bolt12/validate.go
Comment on lines +254 to +258
// signature is intentionally omitted from this list: the spec's
// unsigned offerless variant conflicts with the unconditional
// reader signature check, and other implementations require a
// signature on every invoice_request. We always sign, so a
// present signature here is expected.

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.

please create an issue on the BOLT repo so that it is not forgotten

Comment thread bolt12/invoice_request.go Outdated

if len(metadata) > 0 {
ir.InvreqMetadata = tlv.SomeRecordT(
tlv.RecordT[tlv.TlvType0, tlv.Blob]{

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.

let's do that, I think it makes sense to catch this in the contructor.

Comment thread bolt12/validate.go
Comment thread rpcserver.go
Comment thread bolt12/validate_test.go
@saubyk

saubyk commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

/gateway dismiss F2 per #10832 (comment)

@saubyk

saubyk commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

/gateway dismiss F3 per #10832 (comment)

@lightninglabs-gateway

Copy link
Copy Markdown

🚫 Dismissed F2 (minor) by @saubyk — per #10832 (comment)

Open findings on this PR: 🟡 F1 (minor) · 🟡 F3 (minor)

@lightninglabs-gateway

Copy link
Copy Markdown

🚫 Dismissed F3 (minor) by @saubyk — per #10832 (comment)

Open findings on this PR: 🟡 F1 (minor)

@Abdulkbk Abdulkbk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

This ensures we copy all fields when replying to an offer.
The offer writer rejected a present-but-nil offer_issuer_id with an
ad-hoc error. Introduce a typed ErrNilPublicKey sentinel and use it here
so the rejection is recoverable by callers and reusable by the
invoice_request writer, which guards the same hazard for its own pubkey
fields.
ValidateInvoiceRequestRead and ValidateInvoiceRequestWrite enforce the
structural BOLT 12 requirements an invoice request can be checked
against on its own. The reader validates incoming requests. The writer
catches out-of-range types in decoded-then-mutated requests before they
leave the local boundary. Type 240 carries the signature and sits
outside the allowed range by spec design. Both validators skip it
during the range scan.

Two reader MUSTs are deferred. Schnorr signature verification against
the merkle root keyed by invreq_payer_id lands with the Invoice
message, where the merkle and signing primitives are shared. Offer
cross-validation requires an Offer reference the structural validator
does not carry, and lands in the bolt12handler layer where both the
request and the stored Offer are in scope.
Add a BOLT 12 release note for the invoice_request codec, completing the
offer/invoice_request entries for the bolt12 package in 0.22.0.
@bitromortac

Copy link
Copy Markdown
Collaborator Author

Thanks for all the reviews! I've added also some checks for the nil pubkeys and addressed feedback.

why are you not fixing the linter here tho?

The intention was just to save the additional CI run

@ziggie1984 ziggie1984 enabled auto-merge June 30, 2026 17:22
@ziggie1984 ziggie1984 merged commit c4a67b6 into lightningnetwork:master Jun 30, 2026
41 of 42 checks passed
@github-project-automation github-project-automation Bot moved this from In progress to Done in lnd v0.22 Jun 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bolt12 gateway-active severity-critical Requires expert review - security/consensus critical

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants