Repository navigation
Conversation
In this commit, we bring the decoder in line with three BOLT 11 reader requirements it was lenient about. A fixed length field (`p`, `h`, `s`, `n`) with the wrong `data_length` was treated as absent, so an invoice with a malformed `n` field would silently fall back to public key recovery. BOLT 11 says the reader MUST fail in that case, so `parse32Bytes` and `parseDestination` now return `ErrInvalidFixedFieldLength`. A second `s` field was skipped, which leaves the same first vs last ambiguity for the payment secret that we already closed for the payment hash. Following lightning/bolts#1357, we now reject it with `ErrDuplicatePaymentAddr`. When the invoice carries an explicit `n` field, the signature must be in low-S form. Only the public key recovery path may accept high-S, so we now return `ErrHighSSignature` for a high-S signature verified against `n`. A missing `s` field is still accepted here. lnd creates blinded path invoices without a payment address, and the router already rejects a payment request that has neither.
In this commit, we add the invoice test vectors from lightning/bolts#1357 (`bolt11/invoice-test.json`) and run every one of them through `Decode`, mirroring how the BOLT 12 vectors live under `bolt12/test-vectors`. Each invalid vector has a single defect, so a pass means we reject it for the intended reason. Unknown required feature bits are checked through `WithErrorOnUnknownFeatureBit`, as by default we leave those for pathfinding. The missing `s` vector is the one documented exception: we assert it decodes with no payment address, as that rule is enforced by the router.
🟡 PR Severity: MEDIUM
🟡 Medium (2 files)
🟢 Low (4 files)
AnalysisThis PR is confined to the To override, add a |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
In this PR, we tighten the BOLT 11 decoder in three places where it was more lenient than the spec, and add the spec's invoice test vectors from lightning/bolts#1357 as a test that runs every one of them.
Running those vectors against
zpay32.Decodeshowed that we accepted five invoices the spec says a reader must reject. Three of them are fixed in the decoder:p,h,s,n) with the wrongdata_lengthwas treated as absent rather than failing the invoice. Fornthis meant we'd silently fall back to public key recovery. We now returnErrInvalidFixedFieldLength.sfield was skipped, which leaves the same first vs last ambiguity for the payment secret that zpay32: reject duplicate payment hash fields #11190 closed for the payment hash. bolts#1357 now requires exactly onepand exactly ones, so we returnErrDuplicatePaymentAddr.nfield. Only the public key recovery path may accept high-S, so we now returnErrHighSSignature.The other two are already enforced, just later on purpose. An unknown required feature bit is rejected by pathfinding by default, so that
decodepayreqcan still show the invoice. The test usesWithErrorOnUnknownFeatureBitto check it at decode time. A missingsfield stays legal forDecode, as we create blinded path invoices without a payment address. The router already rejects a payment request that has neither, and the test documents that exception.Compatibility
All three new rejections only affect invoices lnd never produces: our encoder always writes the fixed lengths, a single
sfield, and low-S signatures. Stored invoices that we decode forListInvoicesare our own, so they're unaffected. A third party invoice that relied on one of these leniencies will now fail to decode. Under the spec it was never valid.The vectors live in
zpay32/test-vectors/invoice-test.json, mirroringbolt12/test-vectors. They come from the bolts#1357 branch, so we should resync the file once that PR lands, in case it changes further.