Skip to content

Onion message forwarding #10089

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

gijswijs
Copy link
Collaborator

With this PR we add basic forwarding functionality for onion messages. It builds on PR #9868.

It adds OnionMessagePayload struct to the lnwire package.
It also depends on the not yet merged (PR 68)[https://github.com/lightningnetwork/lightning-onion/pull/68] in the lightning-onion package. For now it uses that package from a forked version.

The msgmux endpoint for onion messages is updated to parse the onion message packet, and forward the onion based on the acquired information.

The SubscribeOnionMessages endpoint is updated to pass along any decrypted information. This endpoint is currently solely meant for itests, although it could have practical use in the future.

gijswijs added 9 commits July 17, 2025 21:01
This message type is a message that carries an onion-encrypted payload
used for BOLT12 messages.
This commit creates the necessary endpoints for onion messages.
Specifically, it adds the following:

- `SendOnionMessage` endpoint to send onion messages.
- `SubscribeOnionMessages` endpoint to subscribe to incoming onion
  messages.

It uses the `msgmux` package to handle the onion messages.
The new wire message defines the OnionMessagePayload, FinalHopPayload,
ReplyPath, and related TLV encoding/decoding logic.
This commit replaces the lightning-onion library so that it points to
the custom fork that alreadys support onion messaging.

This commit should be dropped in favour of a version bump of
lightning-onion once the PR for onion messaging is merged.
With this commit we implement the logic to parse, decrypt, and forward
onion messages. It contains a refactor to its constructor to accept
dependencies like the onionProcessor and a message sender function.

In brontide.go and server.go it adds the plumbing to for passing through
the onionProcessor from the hop iterator and the SendOnionMessage
function to the OnionEndpoint's constructor.
Adds the NewNonFinalBlindedRouteDataOnionMessage function to create
blinded route data specifically for onion messages.
With this Update we change the SubscribeOnionMessages RPC to return a
stream of OnionMessageUpdate messages instead of OnionMessage. This way
we also send back the decrypted payload if any, so we can inspect that
in itests.
Adds the new integration test file to test forwarding of onion messages
through a multi-hop path.
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.

Summary of Changes

Hello @gijswijs, 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 functionality for forwarding onion messages within the Lightning Network. It integrates a new OnionMessage wire protocol message, enables its routing through the existing Sphinx onion processing infrastructure, and provides RPC endpoints for sending and subscribing to these messages. This work lays the groundwork for future application-layer protocols like BOLT12 offers, allowing for privacy-preserving communication beyond just payments.

Highlights

  • Onion Message Wire Protocol: Introduces a new lnwire.OnionMessage type to the wire protocol, enabling the transmission of onion-encrypted messages between Lightning Network nodes. This message type is distinct from HTLC-carrying onion packets and is designed for application-layer communication.
  • Sphinx Onion Processing Integration: Extends the existing Sphinx onion processing logic within htlcswitch/hop to correctly parse and handle OnionMessage payloads. This includes adapting the hop iterator to recognize onion messages, extracting forwarding information (such as the NextNodeID for node-based routing), and performing message-specific TLV validations.
  • New RPC Endpoints for Onion Messages: Adds SendOnionMessage and SubscribeOnionMessages to the LND RPC API. These new endpoints allow external applications and users to programmatically send onion messages to peers and subscribe to a stream of incoming onion messages, facilitating the development of new privacy-preserving communication features.
  • Dedicated Message Endpoint and Forwarding Logic: Implements a new onion_message.OnionEndpoint that integrates with the msgmux to process incoming lnwire.OnionMessages. This endpoint is responsible for decrypting the onion blob, determining if the message is for the local node or needs forwarding, and then either dispatching it to subscribers or relaying it to the next hop in the blinded path.
  • Enhanced Blinded Path Handling: Introduces specific logic for blinded paths within onion messages, including the addition of NextNodeID to ForwardingInfo. This ensures that onion messages, which do not carry payment-related information like CLTV deltas or amounts, are correctly processed and forwarded along their intended blinded routes.
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 in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

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 issue 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 is currently in preview and 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 to provide feedback.

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 introduces onion message forwarding, a significant feature that touches multiple parts of the codebase. The changes are generally well-structured, with new functionality encapsulated in the onion_message package and corresponding updates to lnwire, htlcswitch, and the RPC layer. The inclusion of integration tests for both direct and forwarded onion messages is a great addition. I've identified a few issues that need attention: a critical bug in handling dummy hops for onion messages, a high-severity issue with TLV decoding that could lead to panics, and a medium-severity issue regarding message routing logic that could cause confusion and potential bugs. Addressing these will improve the correctness and maintainability of the new functionality.

Comment on lines +423 to 425
iterator := makeSphinxHopIterator(
r.router, onionPkt, sphinxPacket, blindingKit, r.rHash, false,
)

Choose a reason for hiding this comment

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

critical

The isOnionMessage flag is hardcoded to false when creating a new hop iterator for a peeled dummy hop. This is incorrect, as the peeled hop is still part of the original onion message. This will cause incorrect payload parsing for any subsequent hops within the same onion message, leading to forwarding failures.

Suggested change
iterator := makeSphinxHopIterator(
r.router, onionPkt, sphinxPacket, blindingKit, r.rHash, false,
)
iterator := makeSphinxHopIterator(
r.router, onionPkt, sphinxPacket, blindingKit, r.rHash, isOnionMessage,
)

func decodeReplyPath(r io.Reader, val interface{}, buf *[8]byte,
l uint64) error {

if p, ok := val.(*ReplyPath); ok && l > 35 {

Choose a reason for hiding this comment

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

high

The length check l > 35 for decoding a ReplyPath is too loose and could lead to a panic if a malformed payload is received. A ReplyPath has a fixed-size component of 67 bytes (two 33-byte public keys and a 1-byte hop count). Since a reply path must contain at least one hop, and the minimum size of a hop is 35 bytes, the minimum total length is 102 bytes. A more accurate check would be to ensure the length is at least 67 bytes to cover the fixed-size part before attempting to decode it.

Suggested change
if p, ok := val.(*ReplyPath); ok && l > 35 {
if p, ok := val.(*ReplyPath); ok && l >= 67 {

Comment on lines +2234 to 2236
*lnwire.OnionMessage:

discStream.AddMsg(msg)

Choose a reason for hiding this comment

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

medium

This case statement handles lnwire.OnionMessage by sending it to the discStream, which is intended for gossip messages. However, onion messages should be handled by the onionMessageEndpoint via the msgRouter before this switch statement is reached. This creates a confusing situation. If the router handles the message correctly, this code is unreachable. If the router fails for some reason, the message will be incorrectly routed to the gossiper. This case should be removed to avoid potential bugs and improve clarity.

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.

1 participant