Skip to content
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

BOLT 7: Onion message support (features 38/39) #759

Merged
merged 3 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .aspell.en.pws
Original file line number Diff line number Diff line change
Expand Up @@ -387,9 +387,10 @@ CHECKSIGVERIFY
IFDUP
sats
anysegwit
onionmsg
griefing
unspendable
pkh
kB
unblind
unblinded
unblinded
170 changes: 170 additions & 0 deletions 04-onion-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ A node:
* [Returning Errors](#returning-errors)
* [Failure Messages](#failure-messages)
* [Receiving Failure Codes](#receiving-failure-codes)
* [Onion Messages](#onion-messages)
* [Test Vector](#test-vector)
* [Returning Errors](#returning-errors)
* [References](#references)
Expand Down Expand Up @@ -1406,6 +1407,175 @@ The _origin node_:
- MAY use the data specified in the various failure types for debugging
purposes.

# Onion Messages

Onion messages allow peers to use existing connections to query for
invoices (see [BOLT 12](12-offer-encoding.md)). Like gossip messages,
they are not associated with a particular local channel. Like HTLCs,
they use [onion messages](#onion-messages) protocol for
end-to-end encryption.

Onion messages use the same form as HTLC `onion_packet`, with a
slightly more flexible format: instead of 1300 byte payloads, the
payload length is implied by the total length (minus 66 bytes for the
header and trailing bytes). The `onionmsg_payloads` themselves are the same
as the `hop_payloads` format, except there is no "legacy" length: a 0
`length` would mean an empty `onionmsg_payload`.

Onion messages are unreliable: in particular, they are designed to
Copy link

Choose a reason for hiding this comment

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

Since OMs are unreliable and a sender might have to retry multiple times (maybe even try numerous paths simultaneously to gain more reliability), does it make sense to add an idempotency key to onion messages?

be cheap to process and require no storage to forward. As a result,
there is no error returned from intermediary nodes.

For consistency, all onion messages use [Route Blinding](#route-blinding).

## The `onion_message` Message

1. type: 513 (`onion_message`) (`option_onion_messages`)
2. data:
* [`point`:`blinding`]
* [`u16`:`len`]
* [`len*byte`:`onion_message_packet`]

1. type: `onion_message_packet`
2. data:
* [`byte`:`version`]
* [`point`:`public_key`]
* [`...*byte`:`onionmsg_payloads`]
* [`32*byte`:`hmac`]

1. type: `onionmsg_payloads`
2. data:
* [`bigsize`:`length`]
* [`length*u8`:`onionmsg_tlv`]
* [`32*byte`:`hmac`]
* ...
* `filler`

The `onionmsg_tlv` itself is a TLV: an intermediate node expects an
`encrypted_data` which it can decrypt into an `encrypted_data_tlv`
using the `blinding` which it is handed along with the onion message.

Field numbers 64 and above are reserved for payloads for the final
hop, though these are not explicitly refused by non-final hops (unless
even, of course!).

1. `tlv_stream`: `onionmsg_tlv`
2. types:
1. type: 2 (`reply_path`)
2. data:
* [`blinded_path`:`path`]
1. type: 4 (`encrypted_recipient_data`)
2. data:
* [`...*byte`:`encrypted_recipient_data`]

1. subtype: `blinded_path`
2. data:
* [`point`:`first_node_id`]
* [`point`:`blinding`]
* [`byte`:`num_hops`]
* [`num_hops*onionmsg_hop`:`path`]

1. subtype: `onionmsg_hop`
2. data:
* [`point`:`blinded_node_id`]
* [`u16`:`enclen`]
* [`enclen*byte`:`encrypted_recipient_data`]

#### Requirements

The creator of `encrypted_recipient_data` (usually, the recipient of the onion):

- MUST create the `encrypted_recipient_data` from the `encrypted_data_tlv` as required in [Route Blinding](#route-blinding).
- MUST NOT include `short_channel_id`, `payment_relay` or `payment_constraints` in any `encrypted_data_tlv`
- MUST include `encrypted_data_tlv.next_node_id` for each non-final node.
- MUST create the `encrypted_recipient_data` from the `encrypted_data_tlv` as required in [Route Blinding](#route-blinding).

The writer:

- MUST set the `onion_message_packet` `version` to 0.
- MUST construct the `onion_message_packet` `onionmsg_payloads` as detailed above using Sphinx.
- MUST NOT use any `associated_data` in the Sphinx construcion.
- SHOULD set `onion_message_packet` `len` to 1366 or 32834.

Choose a reason for hiding this comment

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

Does this mean that an OM can travel far more than 20 hops?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Potentially, yes. With varonions the limitation to fixed 20 hops went away anyway, so even the smaller 1366 byte onion could potentially contain more hops. It all deppends on the payload you want to deliver at each hop.

Copy link

@bshramin bshramin Aug 15, 2023

Choose a reason for hiding this comment

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

Thank you for the answer. So if I want to calculate the maximum number of hops I can go through, does this look right?
32768 / 70 =~ 468 hops

- SHOULD retry via a different path if it expects a response and doesn't receive one after a reasonable period.
- For the non-final nodes' `onionmsg_tlv`:
- MUST NOT set fields other than `encrypted_recipient_data`.
- For the final node's `onionmsg_tlv`:
- if the final node is permitted to reply:
- MUST set `reply_path` `blinding` to the initial blinding factor for the `first_node_id`
- MUST set `reply_path` `first_node_id` to the unblinded node id of the first node in the reply path.
- For every `reply_path` `path`:
- MUST set `blinded_node_id` to the blinded node id to encrypt the onion hop for.
- MUST set `encrypted_recipient_data` to a valid encrypted `encrypted_data_tlv` stream which meets the requirements of the `onionmsg_tlv` when used by the recipient.
- MAY use `path_id` to contain a secret so it can recognize use of this `reply_path`.
- otherwise:
- MUST NOT set `reply_path`.


The reader:

- SHOULD accept onion messages from peers without an established channel.
rustyrussell marked this conversation as resolved.
Show resolved Hide resolved
- MAY rate-limit messages by dropping them.
- MUST read the `encrypted_recipient_data` using `blinding` as required in [Route Blinding](#route-blinding).
- MUST ignore the message if that considers the message invalid.
- if `encrypted_data_tlv` contains `allowed_features`:
- MUST ignore the message if:
- `encrypted_data_tlv.allowed_features.features` contains an unknown feature bit (even if it is odd).
- the message uses a feature not included in `encrypted_data_tlv.allowed_features.features`.
- if it is not the final node according to the onion encryption:
- if the `onionmsg_tlv` contains other tlv fields than `encrypted_recipient_data`:
- MUST ignore the message.
- if the `encrypted_data_tlv` contains `path_id`:
- MUST ignore the message.
- otherwise:
- SHOULD forward the message using `onion_message` to the next peer indicated by `next_node_id`.
- if it forwards the message:
- MUST set `blinding` in the forwarded `onion_message` to the next blinding as calculated in [Route Blinding](#route-blinding).
- otherwise (it is the final node):
- if `path_id` is set and corresponds to a path the reader has previously published in a `reply_path`:
- if the onion message is not a reply to that previous onion:
- MUST ignore the onion message
- otherwise (unknown or unset `path_id`):
- if the onion message is a reply to an onion message which contained a `path_id`:
- MUST respond (or not respond) exactly as if it did not send the initial onion message.
- if the `onionmsg_tlv` contains more than one payload field:
- MUST ignore the message.
- if it wants to send a reply:
- MUST create an onion message using `reply_path`.
- MUST send the reply via `onion_message` to the node indicated by
the `first_node_id`, using `reply_path` `blinding` to send
along `reply_path` `path`.


#### Rationale

Care must be taken that replies are only accepted using the exact
reply_path given, otherwise probing is possible. That means checking
both ways: non-replies don't use the reply path, and replies always
use the reply path.

The requirement to discard messages with `onionmsg_tlv` fields which
are not strictly required ensures consistency between current and
future implementations. Even odd fields can be a problem since they
are parsed (and thus may be rejected!) by nodes which understand them,
and ignored by those which don't.

All onion messages are blinded, even though this overhead is not
always necessary (33 bytes here, the 16-byte MAC for each encrypted_data_tlv in
the onion). This blinding allows nodes to use a path provided by
others without knowing its contents. Using it universally simplifies
implementations a little, and makes it more difficult to distinguish
onion messages.

`len` allows larger messages to be sent than the standard 1300 bytes
allowed for an HTLC onion, but this should be used sparingly as it
reduces the anonymity set, hence the recommendation that it either looks
like an HTLC onion, or if larger, be a fixed size.

Onion messages don't explicitly require a channel, but for
spam-reduction a node may choose to ratelimit such peers, especially
messages it is asked to forward.


# Test Vector

## Returning Errors
Expand Down
1 change: 1 addition & 0 deletions 09-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ The Context column decodes as follows:
| 22/23 | `option_anchors_zero_fee_htlc_tx` | Anchor commitment type with zero fee HTLC transactions | IN | `option_static_remotekey` | [BOLT #3][bolt03-htlc-tx], [lightning-dev][ml-sighash-single-harmful] |
| 24/25 | `option_route_blinding` | Node supports blinded paths | IN9 | `var_onion_optin` | [BOLT #4](bolt04-route-blinding) |
| 26/27 | `option_shutdown_anysegwit` | Future segwit versions allowed in `shutdown` | IN | | [BOLT #2][bolt02-shutdown] |
| 38/39 | `option_onion_messages` | Can forward onion messages | IN | | [BOLT #7](04-onion-routing.md#onion-messages) |
| 44/45 | `option_channel_type` | Node supports the `channel_type` field in open/accept | IN | | [BOLT #2](02-peer-protocol.md#the-open_channel-message) |
| 46/47 | `option_scid_alias` | Supply channel aliases for routing | IN | | [BOLT #2][bolt02-channel-ready] |
| 48/49 | `option_payment_metadata` | Payment metadata in tlv record | 9 | | [BOLT #11](11-payment-encoding.md#tagged-fields) |
Expand Down
Loading