bolt4

Onion routing protocol, per BOLT #4 (docs.ppad.tech/bolt4).
git clone git://git.ppad.tech/bolt4.git
Log | Files | Refs | README | LICENSE

README.md (5966B)


      1 # ppad-bolt4
      2 
      3 [![](https://img.shields.io/hackage/v/ppad-bolt4?color=blue)](https://hackage.haskell.org/package/ppad-bolt4)
      4 ![](https://img.shields.io/badge/license-MIT-brightgreen)
      5 [![](https://img.shields.io/badge/haddock-bolt4-lightblue)](https://docs.ppad.tech/bolt4)
      6 
      7 A pure Haskell implementation of Lightning Network onion routing,
      8 [BOLT #4][bolt4]: onion construction and processing, returning errors,
      9 and route blinding.
     10 
     11 ## Usage
     12 
     13 A sample GHCi session:
     14 
     15 ```
     16   > :set -XOverloadedStrings
     17   > import qualified Data.ByteString as BS
     18   > import qualified Lightning.Protocol.BOLT1 as BOLT1
     19   > import Lightning.Protocol.BOLT4
     20   >
     21   > -- keys (use a fresh random session key for every onion)
     22   > let Just session = secret_key (BS.replicate 32 0x41)
     23   > let Just alice = secret_key (BS.replicate 32 0x42)
     24   > let Just bob = secret_key (BS.replicate 32 0x43)
     25   >
     26   > -- a two-hop route, paying bob through alice
     27   > let Just amt = BOLT1.milli_satoshi 100000
     28   > let Just scid = BOLT1.short_channel_id 800000 1 0
     29   > let pay = empty_hop_payload { hp_amt_to_forward = Just amt }
     30   > :{
     31   ghci| let to_alice = Hop (public_key alice) pay {
     32   ghci|         hp_outgoing_cltv_value = Just 800040
     33   ghci|       , hp_short_channel_id    = Just scid
     34   ghci|       }
     35   ghci|     to_bob = Hop (public_key bob) pay {
     36   ghci|         hp_outgoing_cltv_value = Just 800000
     37   ghci|       }
     38   ghci| :}
     39   > let hash = BS.replicate 32 0xff
     40   > let Right (onion, secrets) = construct session [to_alice, to_bob] hash
     41   > BS.length (encode_onion_packet onion)
     42   1366
     43   >
     44   > -- each hop peels off its layer
     45   > let Right (Forward fwd) = process alice onion hash Nothing
     46   > hp_short_channel_id (fwd_payload fwd)
     47   Just (ShortChannelId 879609302220865536)
     48   > let next = fwd_next_packet fwd
     49   > let Right (Receive rcv) = process bob next hash Nothing
     50   > hp_amt_to_forward (rcv_payload rcv)
     51   Just (MilliSatoshi 100000)
     52   >
     53   > -- bob fails the payment, alice wraps the error, and the sender
     54   > -- attributes it to bob
     55   > let failure = FailureMessage MppTimeout BOLT1.empty_tlv_stream
     56   > let Right err = construct_error (rcv_shared_secret rcv) failure
     57   > unwrap_error secrets (wrap_error (fwd_shared_secret fwd) err)
     58   Attributed 1 (FailureMessage {fm_failure = MppTimeout,
     59     fm_tlvs = TlvStream []})
     60   >
     61   > -- bob hands out a blinded route to himself
     62   > let Just seed = secret_key (BS.replicate 32 0x01)
     63   > let mine = empty_blinded_hop_data { bhd_path_id = Just "invoice 1" }
     64   > let Right path = create_blinded_path seed [(public_key bob, mine)]
     65   > let [hop] = bp_hops path
     66   >
     67   > -- the sender reaches the route's introduction node through alice
     68   > :{
     69   ghci| let blinded = Hop (bp_first_node_id path) pay {
     70   ghci|         hp_outgoing_cltv_value = Just 800000
     71   ghci|       , hp_total_amount_msat   = Just amt
     72   ghci|       , hp_encrypted_data      = Just (bh_encrypted_data hop)
     73   ghci|       , hp_current_path_key    = Just (bp_first_path_key path)
     74   ghci|       }
     75   ghci| :}
     76   > let Right (onion', _) = construct session [to_alice, blinded] hash
     77   > let Right (Forward fwd') = process alice onion' hash Nothing
     78   > let next' = fwd_next_packet fwd'
     79   > let Right (Receive rcv') = process bob next' hash Nothing
     80   > fmap (bhd_path_id . bi_data) (rcv_blinded rcv')
     81   Just (Just "invoice 1")
     82 ```
     83 
     84 Nodes further along a blinded route pass the `path_key` they received
     85 (`bi_next_path_key` of the previous hop) as the last argument of
     86 `process`, which then derives their blinded private key.
     87 
     88 ## Documentation
     89 
     90 Haddocks (API documentation, etc.) are hosted at
     91 [docs.ppad.tech/bolt4](https://docs.ppad.tech/bolt4).
     92 
     93 ## Specification
     94 
     95 This library implements BOLT #4 as of lightning/bolts commit
     96 [1aadb719][spec] (2026-09-21); a copy lives in `etc/`. It is tested
     97 against the `onion-test.json`, `onion-error-test.json`,
     98 `route-blinding-test.json` and `blinded-payment-onion-test.json`
     99 vectors, and the 'Returning Errors' trace.
    100 
    101 Fundamental types and the TLV format come from [ppad-bolt1][bolt1];
    102 `allowed_features` is a [ppad-bolt9][bolt9] feature vector.
    103 
    104 Not implemented:
    105 
    106 * Attributable failures (`option_attribution_data`): the
    107   `attribution_data` of `update_fail_htlc` and `update_fulfill_htlc`,
    108   and HTLC hold times.
    109 * Onion messages (`onion_message_packet`, `onionmsg_tlv`).
    110 * The 32 KiB limit on return packets: `construct_error` doesn't enforce
    111   it, and `wrap_error` doesn't truncate longer packets.
    112 * The origin node's dummy decryption rounds: `unwrap_error` stops at the
    113   hop whose HMAC matches.
    114 * The `blinded_path` wire encoding, and the computation of a blinded
    115   route's total fees and CLTV delta.
    116 
    117 `process` enforces the onion decryption requirements and the path key
    118 rules of route blinding; the reader requirements that depend on the
    119 HTLC or the node's channels (required fields, amount and expiry checks,
    120 `payment_relay` and `payment_constraints`, `allowed_features`, replayed
    121 HMACs) are left to the caller.
    122 
    123 ## Performance
    124 
    125 The aim is best-in-class performance for pure Haskell code. Run the
    126 benchmarks with:
    127 
    128 ```
    129 $ cabal bench
    130 ```
    131 
    132 ## Security
    133 
    134 This library has received no security audit. Secret keys, shared
    135 secrets and payment secrets are abstract and kept out of `Show` output;
    136 shared and payment secrets compare in constant time, and `process`
    137 checks onion HMACs in constant time. Scalar multiplications involving
    138 secrets use ppad-secp256k1's constant-time implementation.
    139 
    140 ## Development
    141 
    142 You'll require [Nix][nixos] with [flake][flake] support enabled. Enter a
    143 development shell with:
    144 
    145 ```
    146 $ nix develop
    147 ```
    148 
    149 Then do e.g.:
    150 
    151 ```
    152 $ cabal repl ppad-bolt4
    153 ```
    154 
    155 to get a REPL for the main library.
    156 
    157 [bolt4]: https://github.com/lightning/bolts/blob/master/04-onion-routing.md
    158 [bolt1]: https://git.ppad.tech/bolt1
    159 [bolt9]: https://git.ppad.tech/bolt9
    160 [spec]: https://github.com/lightning/bolts/tree/1aadb719b4007c4cea0ba6e36b08c4fb53788dee
    161 [nixos]: https://nixos.org/
    162 [flake]: https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html