Skip to content

Conversation

@MPins
Copy link
Contributor

@MPins MPins commented Nov 14, 2025

Fixes #10222

This change enforces low-S canonical signatures in BOLT11 invoices when n is present and adds the corresponding Bolts test vectors (PR#1284 and PR#1298).

Depends on btcsuite/btcd#2463

@gemini-code-assist
Copy link

Summary of Changes

Hello @MPins, 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 a critical update to LND's BOLT11 invoice processing by enforcing low-S canonical signatures when the n field is present. This ensures that LND strictly adheres to the BOLT specifications, preventing the acceptance of invoices with non-canonical high-S signatures. The change enhances the network's security and interoperability by standardizing signature validation, complemented by new test vectors to verify the correct implementation of this enforcement.

Highlights

  • BOLT11 Signature Enforcement: LND now enforces low-S canonical signatures for BOLT11 invoices that include the n field, rejecting non-canonical high-S signatures to ensure protocol compliance.
  • BOLT Spec Alignment: This change aligns LND with the latest BOLT specifications regarding signature canonicalization, improving interoperability and security within the Lightning Network.
  • Test Vector Additions: New test cases have been added to validate the enforcement of low-S signatures, including specific tests for high-S signatures in invoices with the n field, ensuring the new logic functions as expected.
  • Release Notes Update: The release notes for version 0.21.0 have been updated to reflect this significant protocol enforcement, and the contributor list has been expanded.
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 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 counter productive. 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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

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.

Copy link

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

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 enforces low-S canonical signatures for BOLT11 invoices when the destination public key is provided in the n field, aligning with recent BOLT spec updates. The changes include the core logic in zpay32/decode.go and corresponding test cases. My review focuses on improving the implementation's maintainability by leveraging existing library functions. Overall, the change is correct and well-tested.

Comment on lines 190 to 192
// Ensure the signature is in canonical low-S form.
sValue := new(big.Int).SetBytes(sigBase256[32:64])
halfOrder := new(big.Int).Rsh(btcec.S256().N, 1)
if sValue.Cmp(halfOrder) > 0 {
return nil, fmt.Errorf("signature is non-canonical " +
"(high-S)")
}

Choose a reason for hiding this comment

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

medium

For maintainability, it's better to use the existing IsCanonical method from the btcec library to check for low-S signatures instead of re-implementing the logic. This makes the code cleaner and relies on the vetted crypto library implementation.

		// Ensure the signature is in canonical low-S form.
		ecdsaSig, ok := signature.(*ecdsa.Signature)
		if !ok {
			// This should not happen for BOLT-11 invoices.
			return nil, fmt.Errorf("signature is not an ECDSA signature")
		}
		if !ecdsaSig.IsCanonical() {
			return nil, fmt.Errorf("signature is non-canonical (high-S)")
		}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

"github.com/btcsuite/btcd/btcec/v2/ecdsa" has no field or method IsCanonical

zpay32/decode.go Outdated
// Ensure the signature is in canonical low-S form.
sValue := new(big.Int).SetBytes(sigBase256[32:64])
halfOrder := new(big.Int).Rsh(btcec.S256().N, 1)
if sValue.Cmp(halfOrder) > 0 {
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps let's get a helper function in btcec?

Copy link
Member

Choose a reason for hiding this comment

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

We can extract it and re-use from here: https://github.com/btcsuite/btcd/blob/9ff0780da683ec1fa88ce9b9f2f4fd39d5592221/txscript/engine.go#L1399-L1412.

Also the bounds on the slice above my not be entirely correct, as the signature encoding itself can be variable size.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A helper function in btcec makes perfect sense; I’ll open a PR for that as well and use it here.

From my understanding, the slice bounds here in LND are not a problem, since the signature uses compact recoverable encoding (32-byte r, 32-byte s, 1-byte recovery_id). This would only be an issue with DER-encoded signatures. Let me know if I’m misunderstanding anything.

This change enforces low-S canonical signatures when `n` is present
and adds the corresponding test vectors.
@MPins
Copy link
Contributor Author

MPins commented Nov 18, 2025

Hello @Roasbeef the helper function was created btcsuite/btcd#2463

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Follow up signature normalization requirements

3 participants