README.md (5966B)
1 # ppad-bolt4 2 3 [](https://hackage.haskell.org/package/ppad-bolt4) 4  5 [](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