Skip to content

onionmessage: graph-based pathfinding for onion messages #10612

Open
Abdulkbk wants to merge 15 commits intolightningnetwork:masterfrom
Abdulkbk:onion-msg-path
Open

onionmessage: graph-based pathfinding for onion messages #10612
Abdulkbk wants to merge 15 commits intolightningnetwork:masterfrom
Abdulkbk:onion-msg-path

Conversation

@Abdulkbk
Copy link
Contributor

Change Description

Part of #10220
Depends on #10089

This PR builds on top of #10089 (onion message forwarding) and is part of the onion message tracking issue #10220. The forwarding layer can relay messages hop-by-hop, but a sender still needs a way to find a path through the graph to a destination. This PR adds that piece.

A FindPath function in onionmessage/pathfind.go that discovers a route from a local node to a destination through nodes that advertise onion message support (feature bit 38/39, OnionMessagesOptional/OnionMessagesRequired).

We use Breadth-First Search (BFS) over hop count. Since onion messages are not priced by fees or liquidity, the meaningful routing metric is path length. Shorter paths reduce the relay burden on intermediate nodes and lower latency. BFS naturally finds the minimum-hop path, which is appropriate for the channel graph. Intermediate nodes are included only if they advertise support for onion messages (bits 39/38).

A set of unit tests is included to test for various scenarios.

Roasbeef and others added 15 commits February 12, 2026 17:13
This commit introduces a new Mailbox interface that abstracts the
message queue implementation for actors. Previously, actors used a
direct channel for their mailbox, which limited flexibility and made
it difficult to implement alternative mailbox strategies.

The new Mailbox interface provides methods for sending, receiving, and
draining messages, with full context support for cancellation. The
Receive method leverages Go 1.23's iter.Seq pattern, providing a clean
iterator-based API that allows natural for-range loops over messages.

The ChannelMailbox implementation maintains the existing channel-based
behavior while conforming to the new interface. It stores the actor's
context internally, ensuring both caller and actor contexts are
properly respected during send and receive operations. This simplifies
context handling compared to complex context merging approaches.

This abstraction enables future implementations such as priority
mailboxes, persistent mailboxes, or bounded mailboxes with overflow
strategies, without requiring changes to the actor implementation.
This commit adds thorough test coverage for the new Mailbox interface
and ChannelMailbox implementation. The tests verify correct behavior
across various scenarios including successful sends, context
cancellation, mailbox closure, and concurrent operations.

The test suite specifically validates that the mailbox respects both
the caller's context and the actor's context during send and receive
operations. This ensures that actors properly shut down when their
context is cancelled, and that callers can cancel operations without
affecting the actor's lifecycle.

Additional tests cover edge cases such as zero-capacity mailboxes
(which default to a capacity of 1), draining messages after closure,
and concurrent sends from multiple goroutines. The concurrent test
uses 10 senders each sending 100 messages to verify thread-safety
and proper message ordering.

All tests pass with the race detector enabled, confirming the
implementation is free from data races.
This commit refactors the Actor implementation to use the new Mailbox
interface instead of directly managing a channel. This change
significantly simplifies the actor's message processing loop and
improves separation of concerns.

The main changes include replacing the direct channel field with a
Mailbox interface, updating NewActor to create a ChannelMailbox
instance, and refactoring the process method to use the iterator
pattern provided by mailbox.Receive. The new implementation uses a
clean for-range loop over the mailbox's message iterator, eliminating
the complex select statement that previously handled both message
reception and context cancellation.

The Tell and Ask methods in actorRefImpl have been simplified to use
the mailbox's Send method, which internally handles both the caller's
context and the actor's context. This eliminates the need for complex
select statements in these methods and ensures consistent context
handling throughout the actor system.

Message draining during shutdown is now handled through the mailbox's
Drain method, providing a cleaner separation between normal message
processing and cleanup operations. The actor still properly sends
unprocessed messages to the Dead Letter Office and completes pending
promises with appropriate errors during shutdown.
The new wire message defines the OnionMessagePayload, FinalHopPayload,
ReplyPath, and related TLV encoding/decoding logic.
Update lightning-onion to commit that includes onion-messaging support.
Adds the NewNonFinalBlindedRouteDataOnionMessage function to create
blinded route data specifically for onion messages.
Initialize a sphinx router without persistent replay protection logging
for onion message processing. Onion messages don't require replay
protection since they don't involve payment routing.
Introduce OnionPeerActor to handle the sending of onion message to each
peer. The actor is registered with the receptionist pattern to enable
message routing through the actor system. Also adds onion message
feature bits to the protocol, so that the actor is only spawned when the
peer supports onion messages.
Add onion message forwarding capability using the OnionPeerActor for
communication. Messages are routed through a receptionist pattern where
each peer has a dedicated OnionPeerActor for handling message sends.

The OnionEndpoint uses the sphinx router for decoding and decrypting the
onion message packet and the encrypted recipient data in the payload of
the onion messages.
Change SubscribeOnionMessages RPC to return OnionMessageUpdate instead
of OnionMessage, including the decrypted payload to enable payload
inspection in integration tests. The encrypted recipient data is still
not decrypted here.
Previously, each peer created its own OnionEndpoint during Start(),
requiring SphinxOnionMsg and OnionMessageServer to be passed through the
peer config. This added unnecessary fields to the peer's config struct.

This commit refactors onion message handling so that:
- The OnionEndpoint is created once at server startup
- The shared endpoint is passed to peers via config
- Peers simply register the endpoint with their message router

This reduces the peer config surface and properly encapsulates the
OnionEndpoint's dependencies (sphinx router, resolver, message server)
at the server level where they naturally belong.
This commit adds a configuration flag to disable onion messaging support.
When set, lnd will:
- Not advertise the onion messages feature bit (39) in init and node
announcements
- Skip creating the OnionEndpoint at server startup
- Not register an onion message handler with peers, so incoming onion
messages are not processed
Add an LRU cache to GraphNodeResolver to avoid repeated database lookups
when resolving SCIDs to node public keys. The cache stores up to 1000
compressed pubkey entries, which is sufficient for typical onion message
forwarding scenarios.

This change also introduces a NewGraphNodeResolver constructor to
properly initialize the cache, replacing direct struct literal usage.
In this commit we add FindPath, a BFS-based shsortest-path
algorithm that finds routes through the channel graph for
onion messages. The search filters nodes by the
OnionMessage feature bits (38/39).

We also add a  unit tests covering: direct neighbor routing,
multi-hop paths, feature-bit filtering, missing destination
nodes, destination without onion support, max hop limits,
cycle handling, and shortest-path selection.
choice of BFS is because there isn't any weight involve.
@gemini-code-assist
Copy link

Summary of Changes

Hello @Abdulkbk, 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 significantly advances onion message capabilities by introducing a graph-based pathfinding mechanism. It refactors the internal actor system for more robust message handling and establishes a dedicated endpoint for processing and forwarding onion messages. These changes lay the groundwork for more sophisticated onion message interactions within the network, allowing nodes to efficiently discover and utilize paths while adhering to new feature signaling standards.

Highlights

  • Onion Message Pathfinding: Introduced a FindPath function in onionmessage/pathfind.go that uses Breadth-First Search (BFS) to discover minimum-hop routes for onion messages. This function filters the channel graph to include only nodes that advertise onion message support (feature bits 38/39).
  • Actor System Refactoring: Refactored the actor package to introduce a Mailbox interface and a ChannelMailbox implementation, improving message queue management and actor lifecycle handling. This change streamlines message processing and draining during actor shutdown.
  • Onion Message Endpoint and Peer Actors: Added a new onionmessage/endpoint.go to handle incoming onion messages, including logic for forwarding messages to the next hop or delivering them locally. This is complemented by onionmessage/actor.go, which introduces OnionPeerActor to manage sending onion messages to specific peers, integrating with the new actor system.
  • New Feature Bits and Protocol Options: Defined OnionMessagesRequired (38) and OnionMessagesOptional (39) feature bits in lnwire/features.go to signal support for onion messaging. A new protocol.no-onion-messages configuration option was added to allow disabling this feature.
  • RPC and Wire Protocol Updates: Updated the lnrpc and lnwire packages to support the new onion message payload structure, including fields for reply paths, encrypted recipient data, and custom TLV records. The SubscribeOnionMessages RPC now returns a more detailed OnionMessageUpdate.
  • Integration Tests: Added comprehensive integration tests (itest/lnd_onion_message_forward_test.go) to validate onion message forwarding across various scenarios, including forwarding by node ID, by Short Channel ID (SCID), and with concatenated blinded paths.

🧠 New Feature in Public Preview: You can now enable Memory 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.

Changelog
  • .golangci.yml
    • Added github.com/lightningnetwork/lightning-onion to replace-local configuration.
  • actor/actor.go
    • Replaced direct channel usage with a new Mailbox interface for actor message queues.
    • Updated NewActor to initialize with NewChannelMailbox.
    • Refactored process, Tell, and Ask methods to leverage the Mailbox interface for message handling and draining.
  • actor/mailbox.go
    • Added new file defining the Mailbox interface and its ChannelMailbox implementation, providing methods for sending, trying to send, receiving, closing, and draining messages.
  • actor/mailbox_test.go
    • Added new file with comprehensive unit tests for the ChannelMailbox implementation, covering send, try send, receive, close, drain, concurrent operations, zero capacity, and actor context cancellation.
  • docs/release-notes/release-notes-0.21.0.md
    • Updated the description for onion messaging forwarding to clarify the new message type and serialization logic.
  • feature/default_sets.go
    • Added lnwire.OnionMessagesOptional to the default feature sets for initialization and node announcements.
  • feature/manager.go
    • Added a NoOnionMessages boolean field to the Config struct to control onion message feature advertisement.
    • Implemented logic in newManager to unset OnionMessagesOptional and OnionMessagesRequired feature bits if NoOnionMessages is enabled.
  • go.mod
    • Updated github.com/lightningnetwork/lightning-onion to v1.3.0.
    • Added github.com/lightningnetwork/lnd/actor v0.0.3 as a new dependency.
    • Updated github.com/cpuguy83/go-md2man/v2 to v2.0.4.
    • Updated github.com/russross/blackfriday/v2 to v2.1.0.
    • Added a replace directive for github.com/lightningnetwork/lnd/actor to point to the local ./actor path.
  • go.sum
    • Updated checksums to reflect changes in go.mod dependencies.
  • htlcswitch/hop/fuzz_test.go
    • Updated sphinx.MaxPayloadSize to sphinx.MaxRoutingPayloadSize in fuzz tests.
    • Changed sphinx.HopPayload to *sphinx.HopPayload in fuzz tests to align with updated lightning-onion library.
  • itest/list_on_test.go
    • Added testOnionMessageForwarding to the list of all integration test cases.
  • itest/lnd_onion_message_forward_test.go
    • Added new file containing integration tests for various onion message forwarding scenarios, including forwarding by node ID, SCID, and concatenated paths.
  • itest/lnd_onion_message_test.go
    • Updated testOnionMessage to use lnrpc.OnionMessageUpdate for subscribing to messages.
    • Modified the test to build a valid blinded onion message using onionmessage.BuildOnionMessage and record.BlindedRouteData.
  • lncfg/protocol.go
    • Added NoOnionMessagesOption boolean field to ProtocolOptions to disable onion message forwarding.
    • Added NoOnionMessages() method to ProtocolOptions to check if onion messaging is disabled.
  • lncfg/protocol_integration.go
    • Added NoOnionMessagesOption boolean field to ProtocolOptions for integration tests.
    • Added NoOnionMessages() method to ProtocolOptions for integration tests.
  • lnrpc/lightning.proto
    • Changed the return type of SubscribeOnionMessages RPC from OnionMessage to OnionMessageUpdate.
    • Renamed OnionMessage message to OnionMessageUpdate.
    • Added reply_path, encrypted_recipient_data, and custom_records fields to OnionMessageUpdate.
  • lnrpc/lightning.swagger.json
    • Updated Swagger definitions to reflect the renaming of lnrpcOnionMessage to lnrpcOnionMessageUpdate.
    • Added reply_path, encrypted_recipient_data, and custom_records properties to the lnrpcOnionMessageUpdate definition.
  • lnrpc/lightning_grpc.pb.go
    • Updated gRPC client and server interfaces (Lightning_SubscribeOnionMessagesClient, Lightning_SubscribeOnionMessagesServer) to use OnionMessageUpdate.
  • lnwire/features.go
    • Added OnionMessagesRequired (38) and OnionMessagesOptional (39) feature bits to lnwire.
    • Mapped new onion message feature bits to their string representations in the Features map.
  • lnwire/onion_msg_payload.go
    • Added new file defining OnionMessagePayload structure for encoding/decoding onion message TLVs.
    • Introduced FinalHopTLV to represent TLVs reserved for the final hop.
    • Implemented encoding and decoding logic for OnionMessagePayload, including handling ReplyPath, EncryptedData, and FinalHopTLVs.
  • msgmux/msg_router.go
    • Updated comment for Router interface, changing MsgRouter to Router.
  • onionmessage/actor.go
    • Added new file defining Request and Response messages for onion peer actors.
    • Introduced OnionPeerActorRef and OnionPeerActor for managing onion message sending to individual peers.
    • Implemented SpawnOnionPeerActor and StopPeerActor functions to manage actor lifecycle for peers.
  • onionmessage/actor_test.go
    • Added new file with unit tests for OnionPeerActor functionality, including message sending, actor lookup, and lifecycle management.
  • onionmessage/endpoint.go
    • Added new file defining OnionEndpoint as a msgmux.Endpoint to handle incoming lnwire.OnionMessages.
    • Implemented SendMessage logic to process onion messages, decrypt blinded hop data, determine routing action (forward or deliver), and send updates to subscribers.
    • Introduced forwardMessage to send processed onion messages to the next hop via OnionPeerActor.
  • onionmessage/endpoint_test.go
    • Added new file with unit tests for OnionEndpoint, covering various routing scenarios like forwarding by node ID, SCID, concatenated paths, and handling invalid messages.
  • onionmessage/errors.go
    • Added new file defining specific error types for onion message operations, such as ErrActorShuttingDown, ErrNextNodeIdEmpty, ErrSCIDEmpty, ErrNilReceptionist, ErrNilRouter, ErrNilResolver, ErrNoPathFound, and ErrDestinationNoOnionSupport.
  • onionmessage/hop.go
    • Added new file defining forwardAction and deliverAction structs for routing decisions.
    • Introduced NodeIDResolver interface for resolving node public keys from SCIDs.
    • Implemented processOnionMessage to decrypt and interpret onion message packets, deriving next hop information and path keys.
  • onionmessage/hop_test.go
    • Added new file with unit tests for processOnionMessage, isForwarding, and deriveNextPathKey functions, covering different routing scenarios and path key derivation logic.
  • onionmessage/onion_endpoint.go
    • Removed file, its functionality was replaced and expanded by onionmessage/endpoint.go.
  • onionmessage/pathfind.go
    • Added new file implementing FindPath function for onion messages, utilizing BFS to find shortest paths based on hop count and filtering by onion message feature support.
  • onionmessage/pathfind_test.go
    • Added new file with unit tests for the FindPath function, covering direct neighbors, multi-hop paths, feature filtering, disconnected graphs, max hop limits, cycles, and shortest path selection.
  • onionmessage/resolver.go
    • Added new file defining GraphNodeResolver to resolve node public keys from SCIDs using the channel graph and an LRU cache for efficiency.
  • onionmessage/resolver_test.go
    • Added new file with unit tests for mockNodeIDResolver to verify SCID to pubkey resolution.
  • onionmessage/test_utils.go
    • Added new file containing helper functions and mock implementations for onion message testing, including mockNodeIDResolver, EncodeBlindedRouteData, BuildBlindedPath, ConcatBlindedPaths, BuildOnionMessage, and PeelOnionLayers.
  • peer/brontide.go
    • Updated Config struct to include SphinxPayment (renamed from Sphinx), OnionEndpoint, and ActorSystem.
    • Modified Start method to spawn an OnionPeerActor for remote peers supporting onion messages and register the OnionEndpoint with the message router.
    • Updated Disconnect method to stop the OnionPeerActor associated with the disconnected peer.
    • Updated addLink to use SphinxPayment for HTLC processing.
    • Added lnwire.OnionMessage handling to messageSummary for better logging.
  • peer/test_utils.go
    • Updated createTestPeer to initialize sphinx.Router, subscribe.Server, actor.ActorSystem, and onionmessage.OnionEndpoint for test peer configurations.
    • Added noopNodeIDResolver for tests that do not require SCID resolution.
  • record/blinded_data.go
    • Added NewNonFinalBlindedRouteDataOnionMessage function to create blinded route data specifically for onion messages, supporting next node ID, short channel ID, blinding override, and features.
  • record/hop.go
    • Added new TLV types for onion message packets: ReplyPathType, EncryptedDataTLVType, InvoiceRequestNamespaceType, InvoiceNamespaceType, and InvoiceErrorNamespaceType.
    • Introduced corresponding New...Record functions for these new onion message TLV types.
  • routing/pathfind.go
    • Updated sphinx.MaxPayloadSize to sphinx.MaxRoutingPayloadSize when checking routing info size limits during pathfinding.
  • routing/route/blindedroute.go
    • Added new file with OnionMessageBlindedPathToSphinxPath function to convert a blinded path for onion messages into a Sphinx PaymentPath, including handling final hop TLVs and reply paths.
  • rpcserver.go
    • Modified SubscribeOnionMessages RPC handler to send lnrpc.OnionMessageUpdate messages, populating new fields like ReplyPath, EncryptedRecipientData, and CustomRecords.
  • sample-lnd.conf
    • Added a new configuration option protocol.no-onion-messages to disable onion message support.
  • server.go
    • Updated the server struct to include sphinxPayment (renamed from sphinx), sphinxOnionMsg, onionEndpoint, and actorSystem.
    • Initialized a dedicated sphinx.Router (sphinxOnionMsg) for onion messages without replay protection.
    • Integrated NoOnionMessages protocol option into feature manager configuration.
    • Created and initialized onionmessage.OnionEndpoint with GraphNodeResolver if onion messaging is not disabled.
    • Updated peerConnected to pass the new SphinxPayment, OnionEndpoint, and ActorSystem to the peer configuration.
    • Ensured proper shutdown of sphinxOnionMsg and actorSystem during server stop.
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.

@Abdulkbk
Copy link
Contributor Author

The newly added commit is 744769f.

@lightninglabs-deploy lightninglabs-deploy added the severity-critical Requires expert review - security/consensus critical label Feb 26, 2026
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 graph-based pathfinding for onion messages and refactors the actor model's mailbox implementation. My review focuses on improving code clarity, documentation, and robustness in the new onionmessage and related packages. I've suggested simplifying a boolean flag in the message endpoint, correcting a function comment to align with the style guide, adding a missing function comment, and adjusting a length check for more precise error handling in payload decoding. Overall, the changes are well-structured and the new functionality is thoroughly tested.

Comment on lines +94 to +96
// NewNonFinalBlindedRouteData creates the data that's provided for hops within
// a blinded route.
func NewNonFinalBlindedRouteDataOnionMessage(

Choose a reason for hiding this comment

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

medium

The comment for this function appears to be a copy of the one for NewNonFinalBlindedRouteData and doesn't accurately describe this new function. According to the repository style guide (lines 17-18), function comments should begin with the function name and describe its purpose. Please update the comment to reflect that this function is for onion messages and can handle both node IDs and SCIDs for the next hop.

Suggested change
// NewNonFinalBlindedRouteData creates the data that's provided for hops within
// a blinded route.
func NewNonFinalBlindedRouteDataOnionMessage(
// NewNonFinalBlindedRouteDataOnionMessage creates the data that's provided for
// hops within a blinded route for an onion message. It can specify the next hop
// by either public key or short channel ID.
func NewNonFinalBlindedRouteDataOnionMessage(
References
  1. Every function must be commented with its purpose and assumptions, and the comment must begin with the function name. (link)

Comment on lines +122 to +123
func findPeerActor(receptionist *actor.Receptionist, pubKey [33]byte,
) fn.Option[actor.ActorRef[*Request, *Response]] {

Choose a reason for hiding this comment

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

medium

This function is missing a comment, which violates the repository style guide (line 17: "Every function must be commented with its purpose and assumptions."). Please add a comment that describes what this function does.

Suggested change
func findPeerActor(receptionist *actor.Receptionist, pubKey [33]byte,
) fn.Option[actor.ActorRef[*Request, *Response]] {
// findPeerActor looks up an onion peer actor in the receptionist using the
// peer's public key.
func findPeerActor(receptionist *actor.Receptionist, pubKey [33]byte,
) fn.Option[actor.ActorRef[*Request, *Response]] {
References
  1. Every function must be commented with its purpose and assumptions. (link)

Comment on lines +157 to +223
var isProcessedSuccessful bool = false

// Handle the routing action.
payload := fn.ElimEither(routingAction,
func(forwardAction forwardAction) *lnwire.OnionMessagePayload {
log.DebugS(logCtx, "Forwarding onion message",
lnutils.LogPubKey("next_node_id",
forwardAction.nextNodeID),
)

err := o.forwardMessage(
ctx, forwardAction.nextNodeID,
forwardAction.nextPathKey,
forwardAction.nextPacket,
)

if err != nil {
log.ErrorS(logCtx, "Failed to forward onion "+
"message", err)
}

isProcessedSuccessful = true

return forwardAction.payload
},
func(deliverAction deliverAction) *lnwire.OnionMessagePayload {
log.DebugS(logCtx, "Delivering onion message to self")
isProcessedSuccessful = true
return deliverAction.payload
})

// Convert peer []byte to [33]byte.
var peerArr [33]byte
copy(peerArr[:], peer)

// Convert path key []byte to [33]byte.
var pathKeyArr [33]byte
copy(pathKeyArr[:], onionMsg.PathKey.SerializeCompressed())

// Create the onion message update to send to subscribers.
update := &OnionMessageUpdate{
Peer: peerArr,
PathKey: pathKeyArr,
OnionBlob: onionMsg.OnionBlob,
}

// If we have a payload, add its contents to our update.
if payload != nil {
customRecords := make(record.CustomSet)
for _, v := range payload.FinalHopTLVs {
customRecords[uint64(v.TLVType)] = v.Value
}
update.CustomRecords = customRecords
update.ReplyPath = payload.ReplyPath
update.EncryptedRecipientData = payload.EncryptedData
}

// Send the update to any subscribers.
if sendErr := o.onionMessageServer.SendUpdate(update); sendErr != nil {
log.ErrorS(logCtx, "Failed to send onion message update",
sendErr)

return false
}

return isProcessedSuccessful
}

Choose a reason for hiding this comment

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

medium

The isProcessedSuccessful variable is initialized to false and then always set to true if the message can be routed. This logic can be simplified by removing the variable and returning true at the end of the function if no errors that warrant a false return are encountered. This would make the code's intent clearer.

	// Handle the routing action.
	payload := fn.ElimEither(routingAction,
		func(forwardAction forwardAction) *lnwire.OnionMessagePayload {
			log.DebugS(logCtx, "Forwarding onion message",
				lnutils.LogPubKey("next_node_id",
					forwardAction.nextNodeID),
			)

			err := o.forwardMessage(
				ctx, forwardAction.nextNodeID,
				forwardAction.nextPathKey,
				forwardAction.nextPacket,
			)

			if err != nil {
				log.ErrorS(logCtx, "Failed to forward onion "+
					"message", err)
			}

			return forwardAction.payload
		},
		func(deliverAction deliverAction) *lnwire.OnionMessagePayload {
			log.DebugS(logCtx, "Delivering onion message to self")
			return deliverAction.payload
		})

	// Convert peer []byte to [33]byte.
	var peerArr [33]byte
	copy(peerArr[:], peer)

	// Convert path key []byte to [33]byte.
	var pathKeyArr [33]byte
	copy(pathKeyArr[:], onionMsg.PathKey.SerializeCompressed())

	// Create the onion message update to send to subscribers.
	update := &OnionMessageUpdate{
		Peer:      peerArr,
		PathKey:   pathKeyArr,
		OnionBlob: onionMsg.OnionBlob,
	}

	// If we have a payload, add its contents to our update.
	if payload != nil {
		customRecords := make(record.CustomSet)
		for _, v := range payload.FinalHopTLVs {
			customRecords[uint64(v.TLVType)] = v.Value
		}
		update.CustomRecords = customRecords
		update.ReplyPath = payload.ReplyPath
		update.EncryptedRecipientData = payload.EncryptedData
	}

	// Send the update to any subscribers.
	if sendErr := o.onionMessageServer.SendUpdate(update); sendErr != nil {
		log.ErrorS(logCtx, "Failed to send onion message update",
			sendErr)

		return false
	}

	return true

// If we have the correct type, and the length is sufficient (first node
// pubkey (33) + blinding point (33) + hop count (1) = 67 bytes), decode
// the reply path.
if p, ok := val.(*sphinx.BlindedPath); ok && l > 67 {

Choose a reason for hiding this comment

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

medium

The check l > 67 is a bit too strict. A reply path payload must be at least 67 bytes. If a payload of exactly 67 bytes is received, this check will fail and a generic TLV decoding error will be returned. However, a 67-byte payload could contain a hop count of 0, which should result in the more specific ErrNoHops error. Changing the check to l >= 67 would allow the function to handle this case more gracefully and provide a more precise error.

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

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

Labels

severity-critical Requires expert review - security/consensus critical

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants