bolt12: add InvoiceRequest codec and structural validators#10832
bolt12: add InvoiceRequest codec and structural validators#10832bitromortac wants to merge 13 commits into
InvoiceRequest codec and structural validators#10832Conversation
Summary of ChangesHello, 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
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 AssistThe 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
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 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
|
There was a problem hiding this comment.
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.
|
🔴 PR Severity: CRITICAL
🔴 Critical (8 files)
🟠 High (1 file)
🟡 Medium (12 files)
🟢 Low (1 file)
⚪ Excluded from counting (10 files)
Analysis This PR introduces BOLT 12 invoice request codec and structural validators, touching the The new To override, add a |
Add UnsignedRangeFunc and the SerialiseFieldsToSignFn / ExtraSignedFieldsFromTypeMapFn variants so callers with non-BOLT 7 v2 signed ranges (e.g. BOLT 12, which reserves only 240-1000) can plug in their own predicate. The existing SerialiseFieldsToSign and ExtraSignedFieldsFromTypeMap entry points keep their behaviour by delegating to the Fn variants with InUnsignedRange.
This gives us easier optional tlv field handling, which we will use for the following message definitions.
Introduce the canonical lnwire.BlindedPath / BlindedPaths codec with a sealed IntroductionNode sum-type covering both the BOLT 4 pubkey and sciddir variants. The codec gates every variable-length subfield against an io.LimitedReader. It fails closed on the encoder side so invalid input never hits the wire. This commit is a pure addition: no existing caller changes. Subsequent commits migrate OnionMessagePayload and the bolt12 message structs to consume the new codec.
Switch OnionMessagePayload.ReplyPath from *sphinx.BlindedPath to *lnwire.BlindedPath. The reply-path TLV is now produced and consumed by (*lnwire.BlindedPath).Record(), which honours the BOLT 4 sciddir_or_pubkey introduction-node form. The legacy decoder gated on a 67-byte minimum length and silently rejected reply paths whose introduction node used the 9-byte sciddir variant. The legacy replyPathRecord / replyPathSize / encodeReplyPath / decodeReplyPath / blindedHopSize / encodeBlindedHop / decodeBlindedHop helpers and the unused ErrNoHops sentinel are deleted. Consumers update mechanically: routing/route's OnionMessageBlindedPathToSphinxPath replyPath parameter, the onionmessage.OnionMessageUpdate field, the rpcserver onion-message subscription bridge, and the lnwire test utilities now use the lnwire type directly. The new TestOnionMessagePayloadRoundTrip "sciddir intro reply path" subtest pins the BOLT 4 spec fix.
Introduce the ChainsRecord subtype used by the offer_chains and invoice_chains TLV fields. Decoding caps the count at maxOfferChains to bound allocation.
The Offer struct models a long-lived, reusable BOLT 12 payment template. It defines TLV fields as optional records and exposes Encode/DecodeOffer for round-trip serialization. The struct implements lnwire.PureTLVMessage; AllRecords filters the decoded TypeMap through bolt12InUnsignedRange to derive any signed-range extras the encoder must re-emit, keeping offer_id and the Merkle root stable across encoders that understand a wider set of even/odd extensions.
ValidateOfferRead and ValidateOfferWrite enforce the codec-side portion of the BOLT 12 offer reader and writer requirements. Reader rules cover TLV range, even-feature-bit rejection, chain mismatch, dependency rules between offer_amount/description/currency, missing issuer identity, zero-hop blinded paths, and offer expiry. Writer rules mirror the same dependency and identity guards plus a defense-in-depth empty- offer_chains rejection. offer_currency is validated against the ISO 4217 registry via golang.org/x/text/currency (now a direct dependency); offer_issuer_id is verified to be an on-curve SEC1 compressed point on both read and write paths. Encode invokes Validate so invalid bytes never reach the wire.
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.
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.
This ensures we copy all fields when replying to an offer.
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.
38e5fc9 to
d3a1cc6
Compare
Based on #10789 (last five commits), part of #10736.
Adds the BOLT 12
InvoiceRequestmessage struct withPureTLVMessage-basedEncode/Decodeand the structuralValidateInvoiceRequestRead/Writevalidators. Signature verification and offer cross-validation are deferred to theInvoiceandbolt12handlerlayers respectively.I added a commit to address the discussion around the
SubscribeOnionMessagesrpc. There were other requests concerning thelnwireonion message implementation, which I haven't addressed yet. I'll open an extra PR for that.