← Back to Basis of Lightning Technology
BOLT 4specificationlightningpaymentskey-management

BOLT 4: Onion Routing Protocol

## Overview This document describes the construction of an onion routed packet that is used to route a payment from an _origin node_ to a _final node_. The packet is routed through a number of intermediate nodes, called _hops_. The routing schema is based on the [Sphinx][sphinx] construction and is extended with a per-hop payload. Intermediate nodes forwarding the message can verify the integri

No reviews
Unknown·Updated Mar 28, 2026·0 reviews·0 attestations·View source
Collections:BOLTs — Merged

Specification

BOLT #4: Onion Routing Protocol

Overview

This document describes the construction of an onion routed packet that is used to route a payment from an origin node to a final node. The packet is routed through a number of intermediate nodes, called hops.

The routing schema is based on the [Sphinx][sphinx] construction and is extended with a per-hop payload.

Intermediate nodes forwarding the message can verify the integrity of the packet and can learn which node they should forward the packet to. They cannot learn which other nodes, besides their predecessor or successor, are part of the packet's route; nor can they learn the length of the route or their position within it. The packet is obfuscated at each hop, to ensure that a network-level attacker cannot associate packets belonging to the same route (i.e. packets belonging to the same route do not share any correlating information). Notice that this does not preclude the possibility of packet association by an attacker via traffic analysis.

The route is constructed by the origin node, which knows the public keys of each intermediate node and of the final node. Knowing each node's public key allows the origin node to create a shared secret (using ECDH) for each intermediate node and for the final node. The shared secret is then used to generate a pseudo-random stream of bytes (which is used to obfuscate the packet) and a number of keys (which are used to encrypt the payload and compute the HMACs). The HMACs are then in turn used to ensure the integrity of the packet at each hop.

Each hop along the route only sees an ephemeral key for the origin node, in order to hide the sender's identity. The ephemeral key is blinded by each intermediate hop before forwarding to the next, making the onions unlinkable along the route.

This specification describes version 0 of the packet format and routing mechanism.

A node:

  • upon receiving a higher version packet than it implements:
    • MUST report a route failure to the origin node.
    • MUST discard the packet.

Table of Contents

Conventions

There are a number of conventions adhered to throughout this document:

  • HMAC: the integrity verification of the packet is based on Keyed-Hash Message Authentication Code, as defined by the [FIPS 198 Standard][fips198]/[RFC 2104][RFC2104], and using a SHA256 hashing algorithm.
  • Elliptic curve: for all computations involving elliptic curves, the Bitcoin curve is used, as specified in [secp256k1][sec2]
  • Pseudo-random stream: [ChaCha20][rfc8439] is used to generate a pseudo-random byte stream. For its generation, a fixed 96-bit null-nonce (0x000000000000000000000000) is used, along with a key derived from a shared secret and with a 0x00-byte stream of the desired output size as the message.
  • The terms origin node and final node refer to the initial packet sender and the final packet recipient, respectively.
  • The terms hop and node are sometimes used interchangeably, but a hop usually refers to an intermediate node in the route rather than an end node. origin node --> hop --> ... --> hop --> final node
  • The term processing node refers to the specific node along the route that is currently processing the forwarded packet.
  • The term peers refers only to hops that are direct neighbors (in the overlay network): more specifically, sending peers forward packets to receiving peers.
  • Each hop in the route has a variable length hop_payload.
    • The variable length hop_payload is prefixed with a bigsize encoding the length in bytes, excluding the prefix and the trailing HMAC.

Key Generation

A number of encryption and verification keys are derived from the shared secret:

  • rho: used as key when generating the pseudo-random byte stream that is used to obfuscate the per-hop information
  • mu: used during the HMAC generation
  • um: used during error reporting
  • pad: use to generate random filler bytes for the starting mix-header packet

The key generation function takes a key-type (rho=0x72686F, mu=0x6d75, um=0x756d, or pad=0x706164) and a 32-byte secret as inputs and returns a 32-byte key.

Keys are generated by computing an HMAC (with SHA256 as hashing algorithm) using the appropriate key-type (i.e. rho, mu, um, or pad) as HMAC-key and the 32-byte shared secret as the message. The resulting HMAC is then returned as the key.

Notice that the key-type does not include a C-style 0x00-termination-byte, e.g. the length of the rho key-type is 3 bytes, not 4.

Pseudo Random Byte Stream

The pseudo-random byte stream is used to obfuscate the packet at each hop of the path, so that each hop may only recover the address and HMAC of the next hop. The pseudo-random byte stream is generated by encrypting (using ChaCha20) a 0x00-byte stream, of the required length, which is initialized with a key derived from the shared secret and a 96-bit zero-nonce (0x000000000000000000000000).

The use of a fixed nonce is safe, since the keys are never reused.

Packet Structure

The packet consists of four sections:

  • a version byte
  • a 33-byte compressed secp256k1 public_key, used during the shared secret generation
  • a 1300-byte hop_payloads consisting of multiple, variable length, hop_payload payloads
  • a 32-byte hmac, used to verify the packet's integrity

The network format of the packet consists of the individual sections serialized into one contiguous byte-stream and then transferred to the packet recipient. Due to the fixed size of the packet, it need not be prefixed by its length when transferred over a connection.

The overall structure of the packet is as follows:

  1. type: onion_packet
  2. data:
    • [byte:version]
    • [point:public_key]
    • [1300*byte:hop_payloads]
    • [32*byte:hmac]

For this specification (version 0), version has a constant value of 0x00.

The hop_payloads field is a structure that holds obfuscated routing information, and associated HMAC. It is 1300 bytes long and has the following structure:

  1. type: hop_payloads
  2. data:
    • [bigsize:length]
    • [length*byte:payload]
    • [32*byte:hmac]
    • ...
    • filler

Where, the length, payload, and hmac are repeated for each hop; and where, filler consists of obfuscated, deterministically-generated padding, as detailed in Filler Generation. Additionally, hop_payloads is incrementally obfuscated at each hop.

Using the payload field, the origin node is able to specify the path and structure of the HTLCs forwarded at each hop. As the payload is protected under the packet-wide HMAC, the information it contains is fully authenticated with each pair-wise relationship between the HTLC sender (origin node) and each hop in the path.

Using this end-to-end authentication, each hop is able to cross-check the HTLC parameters with the payload's specified values and to ensure that the sending peer hasn't forwarded an ill-crafted HTLC.

Since no payload TLV value can ever be shorter than 2 bytes, length values of 0 and 1 are reserved. (0 indicated a legacy format no longer supported, and 1 is reserved for future use).

payload format

This is formatted according to the Type-Length-Value format defined in BOLT #1.

  1. tlv_stream: payload
  2. types:
    1. type: 2 (amt_to_forward)
    2. data:
      • [tu64:amt_to_forward]
    3. type: 4 (outgoing_cltv_value)
    4. data:
      • [tu32:outgoing_cltv_value]
    5. type: 6 (short_channel_id)
    6. data:
      • [short_channel_id:short_channel_id]
    7. type: 8 (payment_data)
    8. data:
      • [32*byte:payment_secret]
      • [tu64:total_msat]
    9. type: 10 (encrypted_recipient_data)
    10. data:
      • [...*byte:encrypted_recipient_data]
    11. type: 12 (current_path_key)
    12. data:
      • [point:path_key]
    13. type: 16 (payment_metadata)
    14. data:
      • [...*byte:payment_metadata]
    15. type: 18 (total_amount_msat)
    16. data:
      • [tu64:total_msat]

short_channel_id is the ID of the outgoing channel used to route the message; the receiving peer should operate the other end of this channel.

amt_to_forward is the amount, in millisatoshis, to forward to the next receiving peer specified within the routing information, or for the final destination.

For non-final nodes, this includes the origin node's computed fee for the receiving peer, calculated according to the receiving peer's advertised fee schema (as described in BOLT #7).

outgoing_cltv_value is the CLTV value that the outgoing HTLC carrying the packet should have. Inclusion of this field allows a hop to both authenticate the information specified by the origin node, and the parameters of the HTLC forwarded, and ensure the origin node is using the current cltv_expiry_delta value.

If the values don't correspond, this indicates that either a forwarding node has tampered with the intended HTLC values or that the origin node has an obsolete cltv_expiry_delta value.

The requirements ensure consistency in responding to an unexpected outgoing_cltv_value, whether it is the final node or not, to avoid leaking its position in the route.

Requirements

The creator of encrypted_recipient_data (usually, the recipient of payment):

  • MUST create encrypted_data_tlv for each node in the blinded route (including itself).
  • MUST include encrypted_data_tlv.payment_relay for each non-final node.
  • MUST include exactly one of encrypted_data_tlv.short_channel_id or encrypted_data_tlv.next_node_id for each non-final node.
  • MUST set encrypted_data_tlv.payment_constraints for each non-final node and MAY set it for the final node:
    • max_cltv_expiry to the largest block height at which the route is allowed to be used, starting from the final node's chosen max_cltv_expiry height at which the route should expire, adding the final node's min_final_cltv_expiry_delta and then adding encrypted_data_tlv.payment_relay.cltv_expiry_delta at each hop.
    • htlc_minimum_msat to the largest minimum HTLC value the nodes will allow.
  • If it sets encrypted_data_tlv.allowed_features:
    • MUST set it to an empty array.
  • MUST compute the total fees and CLTV delta of the route as follows and communicate them to the sender:
    • total_fee_base_msat(n+1) = (fee_base_msat(n+1) * 1000000 + total_fee_base_msat(n) * (1000000 + fee_proportional_millionths(n+1)) + 1000000 - 1) / 1000000
    • total_fee_proportional_millionths(n+1) = ((total_fee_proportional_millionths(n) + fee_proportional_millionths(n+1)) * 1000000 + total_fee_proportional_millionths(n) * fee_proportional_millionths(n+1) + 1000000 - 1) / 1000000
    • total_cltv_delta = cltv_delta(0) + cltv_delta(1) + ... + cltv_delta(n) + min_final_cltv_expiry_delta
  • MUST create the encrypted_recipient_data from the encrypted_data_tlv as required in Route Blinding.

The writer of the TLV payload:

  • For every node inside a blinded route:
    • MUST include the encrypted_recipient_data provided by the recipient
    • For the first node in the blinded route:
      • MUST include the path_key provided by the recipient in current_path_key
    • If it is the final node:
      • MUST include amt_to_forward, outgoing_cltv_value and total_amount_msat.
      • The value set for outgoing_cltv_value:
        • MUST use the current block height as a baseline value.
        • if a random offset was added to improve privacy:
          • SHOULD add the offset to the baseline value.
    • MUST NOT include any other tlv field.
  • For every node outside of a blinded route:
    • MUST include amt_to_forward and outgoing_cltv_value.
    • For every non-final node:
      • MUST include short_channel_id
      • MUST NOT include payment_data
    • For the final node:
      • MUST NOT include short_channel_id
      • if the recipient provided payment_secret:
        • MUST include payment_data
        • MUST set payment_secret to the one provided
        • MUST set total_msat to the total amount it will send
      • if the recipient provided payment_metadata:
        • MUST include payment_metadata with every HTLC
        • MUST not apply any limits to the size of payment_metadata except the limits implied by the fixed onion size

The reader:

  • If encrypted_recipient_data is present:
    • If path_key is set in the incoming update_add_htlc:
      • MUST return an error if current_path_key is present.
      • MUST use that path_key as path_key for decryption.
    • Otherwise:
      • MUST return an error if current_path_key is not present.
      • MUST use that current_path_key as the path_key for decryption.
      • SHOULD add a random delay before returning errors.
    • MUST return an error if encrypted_recipient_data does not decrypt using the path_key as described in Route Blinding.
    • If payment_constraints is present:
      • MUST return an error if:
        • the expiry is greater than encrypted_recipient_data.payment_constraints.max_cltv_expiry.
        • the amount is below encrypted_recipient_data.payment_constraints.htlc_minimum_msat.
    • If allowed_features is missing:
      • MUST process the message as if it were present and contained an empty array.
    • MUST return an error if:
      • `encrypted_

[Content truncatedview full spec at source]

Discussion (0 threads)

No discussion yet. Be the first to comment.