commit be4f803405adf5b837178103608efe6cd8ade840
parent e4b21c67885bebe6168b5ab374bbac76feeece68
Author: Jared Tobin <jared@jtobin.io>
Date: Sat, 10 Oct 2026 14:57:48 +0400
meta: update etc/04-onion-routing.md to lightning/bolts@1aadb719
Diffstat:
1 file changed, 212 insertions(+), 49 deletions(-)
diff --git a/etc/04-onion-routing.md b/etc/04-onion-routing.md
@@ -63,6 +63,7 @@ A node:
* [Returning Errors](#returning-errors)
* [Failure Messages](#failure-messages)
* [Receiving Failure Codes](#receiving-failure-codes)
+ * [Successful Payments](#successful-payments)
* [Onion Messages](#onion-messages)
* [`max_htlc_cltv` Selection](#max_htlc_cltv-selection)
* [Test Vector](#test-vector)
@@ -98,6 +99,8 @@ There are a number of conventions adhered to throughout this document:
- 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.
+ - These `hop_payload`s are used to build the combined payload in the packet,
+ also referred to as `hop_payloads` or the mix-header.
# Key Generation
@@ -334,8 +337,8 @@ The reader:
- incoming `amount_msat` - `fee` < `amt_to_forward` (where `fee` is the advertised fee as described in [BOLT #7](07-routing-gossip.md#htlc-fees))
- `cltv_expiry` - `cltv_expiry_delta` < `outgoing_cltv_value`
- If it is the final node:
- - MUST treat `total_msat` as if it were equal to `amt_to_forward` if it is not present.
- MUST return an error if:
+ - `total_msat` is not present
- incoming `amount_msat` < `amt_to_forward`.
- incoming `cltv_expiry` < `outgoing_cltv_value`.
- incoming `cltv_expiry` < `current_block_height` + `min_final_cltv_expiry_delta`.
@@ -754,7 +757,8 @@ public keys for `n_1` to `n_r` and generates a random 32-byte `sessionkey`.
Optionally, the sender may pass in _associated data_, i.e. data that the
packet commits to but that is not included in the packet itself. Associated
data will be included in the HMACs and must match the associated data provided
-during integrity verification at each hop.
+during integrity verification at each hop. The `payment_hash` is commonly used for
+this purpose.
To construct the onion, the sender initializes the ephemeral private key for the
first hop `ek_1` to the `sessionkey` and derives from it the corresponding
@@ -796,11 +800,12 @@ following operations:
- The _rho_-key and _mu_-key are generated using the hop's shared secret.
- `shift_size` is defined as the length of the `hop_payload` plus the bigsize encoding of the length and the length of that HMAC. Thus if the payload length is `l` then the `shift_size` is `1 + l + 32` for `l < 253`, otherwise `3 + l + 32` due to the bigsize encoding of `l`.
- - The `hop_payload` field is right-shifted by `shift_size` bytes, discarding the last `shift_size`
+ - The `hop_payloads` field is right-shifted by `shift_size` bytes, discarding the last `shift_size`
bytes that exceed its 1300-byte size.
- - The bigsize-serialized length, serialized `hop_payload` and `hmac` are copied into the following `shift_size` bytes.
- - The _rho_-key is used to generate 1300 bytes of pseudo-random byte stream
- which is then applied, with `XOR`, to the `hop_payloads` field.
+ - The bigsize-serialized length, serialized `hop_payload` and `hmac` are copied into the `shift_size` bytes as the
+ beginning of the mix-header.
+ - The _rho_-key is used to generate a 1300 byte pseudo-random stream
+ which is then applied, with `XOR`, to the `hop_payloads` field.
- If this is the last hop, i.e. the first iteration, then the tail of the
`hop_payloads` field is overwritten with the routing information `filler`.
- The next HMAC is computed (with the _mu_-key as HMAC-key) over the
@@ -816,6 +821,8 @@ the obfuscated `hop_payloads`.
The following Go code is an example implementation of the packet construction:
```Go
+const RoutingInfoSize int32 = 1300
+
func NewOnionPacket(paymentPath []*btcec.PublicKey, sessionKey *btcec.PrivateKey,
hopsData []HopData, assocData []byte) (*OnionPacket, error) {
@@ -849,17 +856,17 @@ func NewOnionPacket(paymentPath []*btcec.PublicKey, sessionKey *btcec.PrivateKey
}
// Generate the padding, called "filler strings" in the paper.
- filler := generateHeaderPadding("rho", numHops, hopDataSize, hopSharedSecrets)
+ filler := generateHeaderPadding(numHops, hopSharedSecrets)
// Allocate and initialize fields to zero-filled slices
- var mixHeader [routingInfoSize]byte
- var nextHmac [hmacSize]byte
+ var mixHeader [RoutingInfoSize]byte
+ var nextHmac [32]byte
- // Our starting packet needs to be filled out with random bytes, we
- // generate some deterministically using the session private key.
- paddingKey := generateKey("pad", sessionKey.Serialize())
- paddingBytes := generateCipherStream(paddingKey, routingInfoSize)
- copy(mixHeader[:], paddingBytes)
+ // Our starting packet needs to be filled out with random bytes, we
+ // generate some deterministically using the session private key.
+ paddingKey := generateKey("pad", sessionKey.Serialize())
+ paddingBytes := generateCipherStream(paddingKey, RoutingInfoSize)
+ copy(mixHeader[:], paddingBytes)
// Compute the routing information for each hop along with a
// MAC of the routing information using the shared key for that hop.
@@ -870,13 +877,13 @@ func NewOnionPacket(paymentPath []*btcec.PublicKey, sessionKey *btcec.PrivateKey
hopsData[i].HMAC = nextHmac
// Shift and obfuscate routing information
- streamBytes := generateCipherStream(rhoKey, numStreamBytes)
+ streamBytes := generateCipherStream(rhoKey, RoutingInfoSize)
rightShift(mixHeader[:], hopDataSize)
buf := &bytes.Buffer{}
hopsData[i].Encode(buf)
copy(mixHeader[:], buf.Bytes())
- xor(mixHeader[:], mixHeader[:], streamBytes[:routingInfoSize])
+ xor(mixHeader[:], mixHeader[:], streamBytes[:RoutingInfoSize])
// These need to be overwritten, so every node generates a correct padding
if i == numHops-1 {
@@ -985,35 +992,42 @@ obfuscating the added `0x00`-bytes at the end.
In order to compute the correct HMAC, the origin node has to pre-generate the
`hop_payloads` for each hop, including the incrementally obfuscated padding added
by each hop. This incrementally obfuscated padding is referred to as the
-`filler`.
+`filler`. Keep in mind that while the mix-header is generated in reverse-route order,
+the filler is generated in route order.
The following example code shows how the filler is generated in Go:
```Go
-func generateFiller(key string, numHops int, hopSize int, sharedSecrets [][sharedSecretSize]byte) []byte {
- fillerSize := uint((numMaxHops + 1) * hopSize)
+const (
+ // The mix-header in Lightning is a max of 1300 bytes
+ NumMaxHops int = 20
+ HopSize = 65
+)
+
+func generateFiller(numHops int, sharedSecrets [][sharedSecretSize]byte) []byte {
+ fillerSize := uint((NumMaxHops + 1) * HopSize)
filler := make([]byte, fillerSize)
// The last hop does not obfuscate, it's not forwarding anymore.
for i := 0; i < numHops-1; i++ {
// Left-shift the field
- copy(filler[:], filler[hopSize:])
+ copy(filler[:], filler[HopSize:])
// Zero-fill the last hop
- copy(filler[len(filler)-hopSize:], bytes.Repeat([]byte{0x00}, hopSize))
+ copy(filler[len(filler)-HopSize:], bytes.Repeat([]byte{0x00}, HopSize))
// Generate pseudo-random byte stream
- streamKey := generateKey(key, sharedSecrets[i])
+ streamKey := generateKey("rho", sharedSecrets[i])
streamBytes := generateCipherStream(streamKey, fillerSize)
// Obfuscate
xor(filler, filler, streamBytes)
}
- // Cut filler down to the correct length (numHops+1)*hopSize
+ // Cut filler down to the correct length (numHops+1)*HopSize
// bytes will be prepended by the packet generation.
- return filler[(numMaxHops-numHops+2)*hopSize:]
+ return filler[(NumMaxHops-numHops+2)*HopSize:]
}
```
@@ -1033,6 +1047,10 @@ besides the origin has access to the information required for its generation.
Note that these error messages are not reliable, as they are not placed on-chain
due to the possibility of hop failure.
+Return packets are limited to 32768 bytes (32 KiB), leaving room in `update_fail_htlc` for `attribution_data` and future
+extensions. Earlier versions permitted larger return packets, so intermediate nodes truncate them to their first 32768
+bytes instead of rejecting the corresponding message.
+
Intermediate hops store the shared secret from the forward path and reuse it to
authenticate and obfuscate any corresponding return packet during each hop.
In addition, each node locally stores data regarding its own sending peer in the
@@ -1118,6 +1136,7 @@ which is in turn applied to the `attribution_data` field using `XOR`.
### Requirements
The _erring node_:
+ - MUST construct the return packet such that its total length is no more than 32768 bytes (32 KiB).
- MUST set `pad` such that the `failure_len` plus `pad_len` is at least 256.
- SHOULD set `pad` such that the `failure_len` plus `pad_len` is equal to
256. Deviating from this may cause older nodes to be unable to parse the
@@ -1164,14 +1183,16 @@ packet. This is then stored as the `reason` field of the `update_htlc_fail` mess
### Requirements
-- if `option_attribution_data` is advertised:
- - if `path_key` is not set in the incoming `update_add_htlc`:
- - if `attribution_data` is received from downstream:
- - MUST transform `attribution_data` as described above
- - otherwise:
- - MUST instantiate an all-zeroes `attribution_data` block
- - MUST update `attribution_data` as described above
-- all nodes:
+The _intermediate node_:
+ - if the return packet received from downstream is longer than 32768 bytes:
+ - MUST truncate it to its first 32768 bytes before transforming the return packet or updating `attribution_data`
+ - if `option_attribution_data` is advertised:
+ - if `path_key` is not set in the incoming `update_add_htlc`:
+ - if `attribution_data` is received from downstream:
+ - MUST transform `attribution_data` as described above
+ - otherwise:
+ - MUST instantiate an all-zeroes `attribution_data` block
+ - MUST update `attribution_data` as described above
- MUST transform the return packet as described above.
- MUST return-forward the `update_htlc_fail` message
@@ -1457,19 +1478,12 @@ An _erring node_ MAY:
A _forwarding node_ MUST:
- if `path_key` is set in the incoming `update_add_htlc`:
- return an `invalid_onion_blinding` error.
- - if `current_path_key` is set in the onion payload and it is not the
- final node:
+ - if `current_path_key` is set in the onion payload:
- return an `invalid_onion_blinding` error.
- otherwise:
- select one of the above error codes when creating an error message.
A _forwarding node_ MAY, but a _final node_ MUST NOT:
- - if the onion `version` byte is unknown:
- - return an `invalid_onion_version` error.
- - if the onion HMAC is incorrect:
- - return an `invalid_onion_hmac` error.
- - if the ephemeral key in the onion is unparsable:
- - return an `invalid_onion_key` error.
- if during forwarding to its receiving peer, an otherwise unspecified,
transient error occurs in the outgoing channel (e.g. channel capacity reached,
too many in-flight HTLCs, etc.):
@@ -1504,7 +1518,15 @@ A _forwarding node_ MAY, but a _final node_ MUST NOT:
- report the current channel setting for the outgoing channel.
- return a `channel_disabled` error.
-An _intermediate hop_ MUST NOT, but the _final node_:
+A _forwarding node_ and a _final node_ MAY:
+ - if the onion `version` byte is unknown:
+ - return an `invalid_onion_version` error.
+ - if the onion HMAC is incorrect:
+ - return an `invalid_onion_hmac` error.
+ - if the ephemeral key in the onion is unparsable:
+ - return an `invalid_onion_key` error.
+
+A _forwarding node_ MUST NOT, but a _final node_ MUST:
- if the payment hash has already been paid:
- MAY treat the payment hash as unknown.
- MAY succeed in accepting the HTLC.
@@ -1583,11 +1605,97 @@ The _origin node_:
- MAY use the data specified in the various failure types for debugging
purposes.
-# Hold times for successful payments
+# Successful Payments
-The `update_fulfill_htlc` message contains an optional `attribution_data` field, similar to the one used in the failure
-case described above. The only difference is that there is no failure message that is covered by the HMACs. With
-attribution data, the sender can obtain hold times as reported and committed to by each hop along the payment path.
+The `update_fulfill_htlc` message contains an optional `attribution_data` field, identical to the one used in the
+failure case described above. With attribution data, the sender can obtain hold times as reported and committed to by
+each hop along the payment path.
+
+In addition, the final node can return data to the origin node in an optional `fulfillment_payload` field. It is the
+success counterpart of the failure message that an erring node returns in the `reason` field of `update_fail_htlc`. The
+final node builds the plaintext as a `fulfillment_payload_tlvs` [TLV stream](01-messaging.md#type-length-value-format):
+
+1. `tlv_stream`: `fulfillment_payload_tlvs`
+2. types:
+ 1. type: 1 (`padding`)
+ 2. data:
+ * [`...*byte`:`padding`]
+
+`padding` conceals the length of the conveyed data and is ignored by the origin. Apart from `padding`, no record types
+are defined yet.
+
+The final node generates a key using key type `fulfillment` and its shared secret with the origin node, then encrypts
+this plaintext with ChaCha20-Poly1305 using that key and an all-zero nonce, as for `encrypted_recipient_data` in route
+blinding. The ciphertext and its 16-byte tag are the `fulfillment_payload`. Each intermediate node then obfuscates the
+`fulfillment_payload` with its `ammag` key, exactly as it obfuscates a failure return packet; the final node does not,
+as the ciphertext is already confidential and forms the innermost layer. The
+`attribution_data` HMACs added by intermediate nodes, which already cover the hold times, now additionally cover the
+`fulfillment_payload`. For each intermediate HMAC, the covered fields are, in order: the `fulfillment_payload` as
+received from the downstream node, before the current node applies its own `ammag` obfuscation; the hold times; and the
+downstream HMACs. The final node's `attribution_data` HMACs do not cover the `fulfillment_payload`. With no payload
+present, there is nothing extra for intermediate nodes to cover. The final node still initializes and updates
+`attribution_data`, and the origin still verifies the final node's `attribution_data` HMACs for its reported hold time.
+
+A successful payment is relayed back with `update_fulfill_htlc`, so a `fulfillment_payload` propagates back through
+every hop, even a blinded path: a blinded final node can originate it, and blinded hops obfuscate and relay it. The
+origin can still recover it, because it shares a secret with every hop, blinded ones included, computed from each hop's
+public key (the blinded public key for a blinded hop) when it built the onion. Returning a `fulfillment_payload` is
+therefore not gated by the `path_key` condition that applies to `attribution_data`.
+
+The origin knows the number of hops in the path, so the final node is its last hop: the origin peels each intermediate
+hop's `ammag` obfuscation, including obfuscation for blinded hops whose blinded public keys it used when constructing
+the route, and decrypts the `fulfillment_payload` with the last hop's `fulfillment` key, one operation per hop. The
+origin verifies the final node's `attribution_data` HMACs without a `fulfillment_payload` input. For each intermediate
+hop, the origin removes that hop's `ammag` obfuscation from the `fulfillment_payload` and verifies the hop's
+`attribution_data` HMACs over the resulting `fulfillment_payload` bytes. A recipient that conceals its position with
+dummy hops in a blinded path must therefore originate the `fulfillment_payload` as though it were the last hop, applying
+the obfuscation for the concealed hops itself, so that this decoding succeeds without revealing its position.
+
+The two integrity checks cover different extents. The Poly1305 tag is verified end-to-end and detects tampering by any
+hop, blinded or not. The `attribution_data` HMACs are contributed only by hops with `path_key` not set, so they
+provide per-hop attribution where available, as in the failure case. This split is intentional: `attribution_data` is
+per-hop data for scoring path nodes, which the origin cannot do for blinded hops it cannot identify and whose hold times
+would aid de-anonymization, whereas a `fulfillment_payload` is end-to-end data that reveals nothing about the
+intermediate hops.
+
+Like return packets, `fulfillment_payload` is limited to 32768 bytes (32 KiB) to leave room for `attribution_data` and
+future extensions. Because `fulfillment_payload` was introduced with this limit, receiving a larger payload is a
+protocol violation as specified in [BOLT #2](02-peer-protocol.md#removing-an-htlc-update_fulfill_htlc-update_fail_htlc-and-update_fail_malformed_htlc).
+
+## Requirements
+
+`attribution_data` is initialized, transformed, updated and verified exactly as in the failure case.
+
+The _final node_:
+ - if `option_attribution_data` is advertised:
+ - MAY include a `fulfillment_payload`, even when reached through a blinded path
+ - if it includes a `fulfillment_payload`:
+ - MUST ensure the `fulfillment_payload` is no more than 32768 bytes (32 KiB)
+ - MUST set `fulfillment_payload_tlvs` to a serialized TLV stream
+ - MUST pad with `padding` such that the serialized `fulfillment_payload_tlvs` stream is at least 256 bytes and a
+ multiple of 256 bytes, excluding the 16-byte Poly1305 tag. The size calculation includes the type and length
+ bytes of the `padding` record; this can require padding to the next multiple of 256 bytes
+ - SHOULD pad such that the serialized `fulfillment_payload_tlvs` stream is exactly 256 bytes if possible
+ - MUST encrypt the plaintext with ChaCha20-Poly1305 as above
+ - MUST NOT include the `fulfillment_payload` in its `attribution_data` HMACs
+ - MUST still initialize and update `attribution_data` for its reported hold time
+
+An _intermediate node_:
+ - if it receives a `fulfillment_payload` from the downstream node:
+ - MUST obfuscate it with its `ammag` key and include it in the outgoing `update_fulfill_htlc`, as it transforms a
+ return packet
+ - MUST include the `fulfillment_payload` as received from the downstream node in its `attribution_data` HMACs
+
+The _origin node_:
+ - MUST recover a `fulfillment_payload` by peeling each intermediate hop's `ammag` obfuscation and decrypting it with
+ the last hop's `fulfillment` key
+ - MUST verify the final node's `attribution_data` HMACs as in the failure case, except that the HMAC input excludes
+ the `fulfillment_payload`, and process its reported hold time
+ - MUST remove an intermediate hop's `ammag` obfuscation from the `fulfillment_payload` before verifying that hop's
+ `attribution_data` HMACs over the resulting `fulfillment_payload` bytes
+ - MUST ignore a `fulfillment_payload` whose Poly1305 tag is invalid, or whose decrypted `fulfillment_payload_tlvs`
+ stream has malformed lengths, duplicate or unordered types, or unknown even types
+ - MUST ignore `padding` records in a valid `fulfillment_payload_tlvs` stream
# Onion Messages
@@ -1638,8 +1746,7 @@ The `onionmsg_tlv` itself is a TLV: an intermediate node expects an
using the `path_key` 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!).
+hop.
1. `tlv_stream`: `onionmsg_tlv`
2. types:
@@ -1690,7 +1797,10 @@ The writer:
The reader:
-- SHOULD accept onion messages from peers without an established channel.
+- if it advertises `option_onion_messages_only_channels`:
+ - MUST NOT accept onion messages from peers without an established channel.
+- otherwise:
+ - SHOULD accept onion messages from peers without an established channel.
- MAY rate-limit messages by dropping them.
- MUST decrypt `onion_message_packet` using an empty `associated_data`, and `path_key`, as described in [Onion Decryption](04-onion-routing.md#onion-decryption) to extract an `onionmsg_tlv`.
- If decryption fails, the result is not a valid `onionmsg_tlv`, or it contains unknown even types:
@@ -1732,6 +1842,19 @@ The reader:
#### Rationale
+Accepting onion messages from channelless peers is the baseline: a node that
+advertises `option_onion_messages` is expected to accept onion messages from
+any connected peer. This is a SHOULD rather than a MUST because acceptance
+is always best effort: a node may still drop messages due to rate-limiting.
+
+A node that wants to restrict acceptance to channel peers (e.g. to limit the
+DoS surface of free bandwidth/CPU for any node that can connect) advertises
+`option_onion_messages_only_channels` as an explicit opt-out. A restrictive
+node should not silently drop these messages while advertising only the
+baseline: it has to advertise the bit, which makes the bit a reliable signal.
+A sender can therefore detect it and simply not attempt a relay it knows will
+fail, rather than sending a message that is silently dropped.
+
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
@@ -1842,7 +1965,8 @@ The following is an in-depth trace of an example of error message creation:
## Returning success
-A successful payment using the parameters above would result in the following attribution data values:
+A successful payment without a `fulfillment_payload` using the parameters above would result in the following
+attribution data values:
attribution data for node 4:
d77d0711b5f71d1d1be56bd88b3bb7ebc1792bb739ea7ebc1bc3b031b8bc2df3a50e25aeb99f47d7f7ab39e24187d3f4df9c4333463b053832ee9ac07274a5261b8b2a01fc09ce9ea7cd04d7b585dfb83299fb6570d71f793c1fcac0ef498766952c8c6840efa02a567d558a3cf6822b12476324b9b9efa03e5f8f26f81fa93daac46cbf00c98e69b6747cf69caaa2a71b025bd18830c4c54cd08f598cfde6197b3f2a951aba907c964c0f5d19a44e6d1d7279637321fa598adde927b3087d238f8b426ecde500d318617cdb7a56e6ce3520fc95be41a549973764e4dc483853ecc313947709f1b5199cb077d46e701fa633e11d3e13b03e9212c115ca6fa004b2f3dd912814693b705a561a06da54cdf603677a3abecdc22c7358c2de3cef771b366a568150aeecc86ad1990bb0f4e2865933b03ea0df87901bff467908273dc6cea31cbab0e2b8d398d10b001058c259ed221b7b55762f4c7e49c8c11a45a107b7a2c605c26dc5b0b10d719b1c844670102b2b6a36c43fe4753a78a483fc39166ae28420f112d50c10ee64ca69569a2f690712905236b7c2cb7ac8954f02922d2d918c56d42649261593c47b14b324a65038c3c5be8d3c403ce0c8f19299b1664bf077d7cf1636c4fb9685a8e58b7029fd0939fa07925a60bed339b23f973293598f595e75c8f9d455d7cebe4b5e23357c8bd47d66d6628b39427e37e0aecbabf46c11be6771f7136e108a143ae9bafba0fc47a51b6c7deef4cba54bae906398ee3162a41f2191ca386b628bde7e1dd63d1611aa01a95c456df337c763cb8c3a81a6013aa633739d8cd554c688102211725e6adad165adc1bcd429d020c51b4b25d2117e8bb27eb0cc7020f9070d4ad19ac31a76ebdf5f9246646aeadbfb9a3f1d75bd8237961e786302516a1a781780e8b73f58dc06f307e58bd0eb1d8f5c9111f01312974c1dc777a6a2d3834d8a2a40014e9818d0685cb3919f6b3b788ddc640b0ff9b1854d7098c7dd6f35196e902b26709640bc87935a3914869a807e8339281e9cedaaca99474c3e7bdd35050bb998ab4546f9900904e0e39135e861ff7862049269701081ebce32e4cca992c6967ff0fd239e38233eaf614af31e186635e9439ec5884d798f9174da6ff569d68ed5c092b78bd3f880f5e88a7a8ab36789e1b57b035fb6c32a6358f51f83e4e5f46220bcad072943df8bd9541a61b7dae8f30fa3dd5fb39b1fd9a0b8e802552b78d4ec306ecee15bfe6da14b29ba6d19ce5be4dd478bca74a52429cd5309d404655c3dec85c252
@@ -1859,6 +1983,45 @@ attribution data for node 1:
attribution data for node 0:
84986c936d26bfd3bb2d34d3ec62cfdb63e0032fdb3d9d75f3e5d456f73dffa7e35aab1db4f1bd3b98ff585caf004f656c51037a3f4e810d275f3f6aea0c8e3a125ebee5f374b6440bcb9bb2955ebf70c06d64090f9f6cf098200305f7f4305ba9e1350a0c3f7dab4ccf35b8399b9650d8e363bf83d3a0a09706433f0adae6562eb338b21ea6f21329b3775905e59187c325c9cbf589f5da5e915d9e5ad1d21aa1431f9bdc587185ed8b5d4928e697e67cc96bee6d5354e3764cede3f385588fa665310356b2b1e68f8bd30c75d395405614a40a587031ebd6ace60dfb7c6dd188b572bd8e3e9a47b06c2187b528c5ed35c32da5130a21cd881138a5fcac806858ce6c596d810a7492eb261bcc91cead1dae75075b950c2e81cecf7e5fdb2b51df005d285803201ce914dfbf3218383829a0caa8f15486dd801133f1ed7edec436730b0ec98f48732547927229ac80269fcdc5e4f4db264274e940178732b429f9f0e582c559f994a7cdfb76c93ffc39de91ff936316726cc561a6520d47b2cd487299a96322dadc463ef06127fc63902ff9cc4f265e2fbd9de3fa5e48b7b51aa0850580ef9f3b5ebb60c6c3216c5a75a93e82936113d9cad57ae4a94dd6481954a9bd1b5cff4ab29ca221fa2bf9b28a362c9661206f896fc7cec563fb80aa5eaccb26c09fa4ef7a981e63028a9c4dac12f82ccb5bea090d56bbb1a4c431e315d9a169299224a8dbd099fb67ea61dfc604edf8a18ee742550b636836bb552dabb28820221bf8546331f32b0c143c1c89310c4fa2e1e0e895ce1a1eb0f43278fdb528131a3e32bfffe0c6de9006418f5309cba773ca38b6ad8507cc59445ccc0257506ebc16a4c01d4cd97e03fcf7a2049fea0db28447858f73b8e9fe98b391b136c9dc510288630a1f0af93b26a8891b857bfe4b818af99a1e011e6dbaa53982d29cf74ae7dffef45545279f19931708ed3eede5e82280eab908e8eb80abff3f1f023ab66869297b40da8496861dc455ac3abe1efa8a6f9e2c4eda48025d43a486a3f26f269743eaa30d6f0e1f48db6287751358a41f5b07aee0f098862e3493731fe2697acce734f004907c6f11eef189424fee52cd30ad708707eaf2e441f52bcf3d0c5440c1742458653c0c8a27b5ade784d9e09c8b47f1671901a29360e7e5e94946b9c75752a1a8d599d2a3e14ac81b84d42115cd688c8383a64fc6e7e1dc5568bb4837358ebe63207a4067af66b2027ad2ce8fb7ae3a452d40723a51fdf9f9c9913e8029a222cf81d12ad41e58860d75deb6de30ad
+A successful payment with a `fulfillment_payload` using the same route parameters contains the following
+`fulfillment_payload_tlvs` records:
+
+ type = 1 (`padding`)
+ value = 245 zero bytes
+
+ type = 65537
+ value = 070809
+
+These two records serialize to 256 bytes: 247 bytes for the `padding` record and 9 bytes for the type `65537` record.
+With the 16-byte Poly1305 tag, this results in a 272-byte `fulfillment_payload`.
+
+The following are the `fulfillment_payload` and attribution data values at each hop:
+
+fulfillment payload for node 4:
+b5b1fed711469adc0da510494818ce54624b537e7a8aba4b190403d90439798a694bdca0d0d53c18487d9940664a8416eb855290621e186081b2222e279bfa6dcb1645a39c2cbc569ed66f8f9ed0c10d4dcbc61abe6685d558d5fb0ec104bf1d14ab1e42a4cf41f1950c3fd06574a8278f553382a78b2725f36559d6db7aeccd41569f88b3722c3535358915648bd2e82c53cbaaa14c34487a77799cc4650d5c2e2edece013cf4095ee99bc96e1a850fa3373538448bd6d5a1771f50ca39b2a4e85c7201be5bedd5fae801e5e99491e6fb1e76457c48094f347205d1170df06e2fa7128d0dfe7d24716fb621632b948f9ad9edba24fba9293906e193a7ca3c234cce42ca9470c08952be1dbd3dcc8452
+attribution data for node 4:
+d77d0711b5f71d1d1be56bd88b3bb7ebc1792bb739ea7ebc1bc3b031b8bc2df3a50e25aeb99f47d7f7ab39e24187d3f4df9c4333463b053832ee9ac07274a5261b8b2a01fc09ce9ea7cd04d7b585dfb83299fb6570d71f793c1fcac0ef498766952c8c6840efa02a567d558a3cf6822b12476324b9b9efa03e5f8f26f81fa93daac46cbf00c98e69b6747cf69caaa2a71b025bd18830c4c54cd08f598cfde6197b3f2a951aba907c964c0f5d19a44e6d1d7279637321fa598adde927b3087d238f8b426ecde500d318617cdb7a56e6ce3520fc95be41a549973764e4dc483853ecc313947709f1b5199cb077d46e701fa633e11d3e13b03e9212c115ca6fa004b2f3dd912814693b705a561a06da54cdf603677a3abecdc22c7358c2de3cef771b366a568150aeecc86ad1990bb0f4e2865933b03ea0df87901bff467908273dc6cea31cbab0e2b8d398d10b001058c259ed221b7b55762f4c7e49c8c11a45a107b7a2c605c26dc5b0b10d719b1c844670102b2b6a36c43fe4753a78a483fc39166ae28420f112d50c10ee64ca69569a2f690712905236b7c2cb7ac8954f02922d2d918c56d42649261593c47b14b324a65038c3c5be8d3c403ce0c8f19299b1664bf077d7cf1636c4fb9685a8e58b7029fd0939fa07925a60bed339b23f973293598f595e75c8f9d455d7cebe4b5e23357c8bd47d66d6628b39427e37e0aecbabf46c11be6771f7136e108a143ae9bafba0fc47a51b6c7deef4cba54bae906398ee3162a41f2191ca386b628bde7e1dd63d1611aa01a95c456df337c763cb8c3a81a6013aa633739d8cd554c688102211725e6adad165adc1bcd429d020c51b4b25d2117e8bb27eb0cc7020f9070d4ad19ac31a76ebdf5f9246646aeadbfb9a3f1d75bd8237961e786302516a1a781780e8b73f58dc06f307e58bd0eb1d8f5c9111f01312974c1dc777a6a2d3834d8a2a40014e9818d0685cb3919f6b3b788ddc640b0ff9b1854d7098c7dd6f35196e902b26709640bc87935a3914869a807e8339281e9cedaaca99474c3e7bdd35050bb998ab4546f9900904e0e39135e861ff7862049269701081ebce32e4cca992c6967ff0fd239e38233eaf614af31e186635e9439ec5884d798f9174da6ff569d68ed5c092b78bd3f880f5e88a7a8ab36789e1b57b035fb6c32a6358f51f83e4e5f46220bcad072943df8bd9541a61b7dae8f30fa3dd5fb39b1fd9a0b8e802552b78d4ec306ecee15bfe6da14b29ba6d19ce5be4dd478bca74a52429cd5309d404655c3dec85c252
+
+fulfillment payload for node 3:
+d4cd5f33730d592c4249f3e31233af0572bf729218ca375ddd8aa518b3fa466d6101f77904b013dd4ef3c912d9278edc09923288c1c6df3fb636053cb6a2fa4bf7e9d7500310a4fc819d4f26a0a03d4fd73c74ab281a2dcf2ec8bb56ecabb1a9884478a172348bf38ddf3dfd579d3c9393dd79a5650da2caedd41fd531fa4ec9f3a46bfc05df7244f30d115687eacc568395e83a165bfe1bc91073af0172e27488771c9fd7744b45c883d538e90bbbc627bcc428c5ebb71fee5877b7a6d23a198a544565dee2fb2ed736b69f8c7c696dd56fd48e88a24017306f74106a0898622abb24fbf6f3b534ff32ceda7d07d05800f9c32730fcb47a5a774cd464503936d2432e8b450ade0cdd14b2ea1bef2e71
+attribution data for node 3:
+1571e10db7f8aa9f8e7e99caaf9c892e106c817df1d8e3b7b0e39d1c48f631e473e17e205489dd7b3c634cac3be0825cbf01418cd46e83c24b8d9c207742db9a0f0e5bcd888086498159f08080ba7bf3bc5cfe92707ca5d3bc81720c889a034b89cf2d376a67c5d63651e3af267efb768a38f488eac2940cf5525e959599984c3cf56c1daaa92476de574582eba16f90b9a6a74f4aa5f1cdad2115c994a09565f9755940ec0665f9e5bc58cad6e523091f94d0bcd3c6c65ca1a5d401128dcc5e14f9108b32e660017c13de598bcf9d403710857cccb0fb9c2a81bfd66bc4552e1132afa3119203a4aaa1e8839c1dab8cbdcde7b527aca3f54bde651aa9f3f2178829cee3f1c0b9292758a40cc63bd998fcd0d3ed4bdcaf1023267b8f8e44130a63ad15f76145936552381eabb6d684c0a3af6ba8efcf207cebaea5b7acdbb63f8e7221102409d10c23f0514dc9f4d0efb2264161a193a999a23e992632710580a0d320f676d367b9190721194514457761af05207cdab2b6328b1b3767eacb36a7ef4f7bd2e16762d13df188e0898b7410f62459458712a44bf594ae662fd89eb300abb6952ff8ad40164f2bcd7f86db5c7650b654b79046de55d51aa8061ce35f867a3e8f5bf98ad920be827101c64fb871d86e53a4b3c0455bfac5784168218aa72cbee86d9c750a9fa63c363a8b43d7bf4b2762516706a306f0aa3be1ec788b5e13f8b24837e53ac414f211e11c7a093cd9653dfa5fba4e377c79adfa5e841e2ddb6afc054fc715c05ddc6c8fc3e1ee3406e1ffceb2df77dc2f02652614d1bfcfaddebaa53ba919c7051034e2c7b7cfaabdf89f26e7f8e3f956d205dfab747ad0cb505b85b54a68439621b25832cbc2898919d0cd7c0a64cfd235388982dd4dd68240cb668f57e1d2619a656ed326f8c92357ee0d9acead3c20008bc5f04ca8059b55d77861c6d04dfc57cfba57315075acbe1451c96cf28e1e328e142890248d18f53b5d3513ce574dea7156cf596fdb3d909095ec287651f9cf1bcdc791c5938a5dd9b47e84c004d24ab3ae74492c7e8dcc1da15f65324be2672947ec82074cac8ce2b925bc555facbbf1b55d63ea6fbea6a785c97d4caf2e1dad9551b7f66c31caae5ebc7c0047e892f201308fcf452c588be0e63d89152113d87bf0dbd01603b4cdc7f0b724b0714a9851887a01f709408882e18230fe810b9fafa58a666654576d8eba3005f07221f55a6193815a672e5db56204053bc4286fa3db38250396309fd28011b5708a26a2d76c4a333b69b6bfd272fb
+
+fulfillment payload for node 2:
+b584abb82973d6112d1474e8685a21253e4936c0b27e7f122c5443694d99bb263e1a8f9724f7cc7b50ce9e79cdbc63738a97bd0d31acee4d15143fea72d1d7dd44c0828a7e3f4fbdc178c2a05caf13fa0eedf325479052aa855aa955e22520fcfbafc5de47d52fc304d4da1b2ba2882861038fc79f6ff6eb0a600e17af44cf25941318a95cb47788e65a4430c9b30d38a2d4e291f3657e5b83712cebeac1f942d4d066f146505d227aefb995cdf098e2e8d82c9118859bf9b1477e3b5bccce02288082bb2e1ded74d94a3278d500298e0a91435a9f6304436a614cfe59e33283067be5bda617e6e094ce861a2c48e51f73ba9dcfab879c6a3a19fbe60697432bb5b04dbc401d53757c133ed01a5ad4ad
+attribution data for node 2:
+34e34397b8621ec2f2b54dbe6c14073e267324cd60b152bce76aec8729a6ddefb61bc263be4b57bd592aae604a32bea69afe6ef4a6b573c26b17d69381ec1fc9b5aa769d148f2f1f8b5377a73840bb6d40734019bf9dcbb731aeac3b1052e6703e8ffa550f2f3b6b40df0faa5bd67271a99a81e81265b567522733358a1c8a3b770d207ba8a22ba238abf056af11042474f4fd44bbb21e3ea8d978058a8cc67a11ad429f816de27a43dd69a3f1fe8ccf670fa081fd9d89af6079f7baf05c7f4bf318b2dcfe18f5bd8628795265f61cbf40b6efa757fc9242927c329f5e06ddbce3be8c1f5dd48a0370eb559f5dc181a09f2209da72f79ae6745992c803310d39f960e8ecf327aed706e4b3e2704eeb9b304dc0e0685f5dcd0389ec377bdba37610ad556a0e957a413a56339dd3c40817214bced5802beee2ee545bdd713208751added5fc0eb2bc89a5aa2decb18ee37dac39f22a33b60cc1a369d24de9f3d2d8b63c039e248806de4e36a47c7a0aed30edd30c3d62debdf1ad82bf7aedd7edec413850d91c261e12beec7ad1586a9ad25b2db62c58ca17119d61dcc4f3e5c4520c42a8e384a45d8659b338b3a08f9e123a1d3781f5fc97564ccff2c1d97f06fa0150cfa1e20eacabefb0c339ec109336d207cc63d9170752fc58314c43e6d4a528fd0975afa85f3aa186ff1b6b8cb12c97ed4ace295b0ef5f075f0217665b8bb180246b87982d10f43c9866b22878106f5214e99188781180478b07764a5e12876ddcb709e0a0a8dd42cf004c695c6fc1669a6fd0e4a1ca54b024d0d80eac492a9e5036501f36fb25b72a054189294955830e43c18e55668337c8c6733abb09fc2d4ade18d5a853a2b82f7b4d77151a64985004f1d9218f2945b63c56fdebd1e96a2a7e49fa70acb4c39873947b83c191c10e9a8f40f60f3ad5a2be47145c22ea59ed3f5f4e61cb069e875fb67142d281d784bf925cc286eacc2c43e94d08da4924b83e58dbf2e43fa625bdd620eba6d9ce960ff17d14ed1f2dbee7d08eceb540fdc75ff06dabc767267658fad8ce99e2a3236e46d2deedcb51c3c6f81589357edebac9772a70b3d910d83cd1b9ce6534a011e9fa557b891a23b5d88afcc0d9856c6dabeab25eea55e9a248182229e4927f268fe5431672fcce52f434ca3d27d1a2136bae5770bb36920df12fbc01d0e8165610efa04794f414c1417f1d4059435c5385bfe2de83ce0e238d6fd2dbd3c0487c69843298577bfa480fe2a16ab2a0e4bc712cd8b5a14871cda61c993b6835303d9043d7689a
+
+fulfillment payload for node 1:
+ba9463d72ce55bc83c9ccd70861ffdf8c5b1a9291be4d9257016ae3c6d3becc6765fe07333369e629ef7475898e535dda560281a4dcf2654362b0421e95aff38e8aeb1298787331bec87815490e534af8de7c85bdf537418fdb81464168cb7c2123ee4186fa6da7cb60583c8ff2ed07982b0ce3e690cb9ba99f186d416bb8adb7f020ea2ef2894bbc732fcd78d1a2c3f7983b85f8b039ae370e1dd062e6d2e6439c003a4d65a55102db383ef1ceb291cd057dfba81396959613104b894e5f4202218b86b3ac9e9cb7b4a10a06669d788fd7d8a9dd832d8996592d17593978d1a500fbe1943c57f004e41eeb3da28e114997aa3b6f183fa2af59b8ad67c1c2753beb5c5fe6f4733980225e8d40d0cfd95
+attribution data for node 1:
+74a4ea61339463642a2182758871b2ea724f31f531aa98d80f1c3043febca41d5ee52e8b1e127e61719a0d078db8909748d57839e58424b91f063c4fbc8a221bef261140e66a9b596ca6d420a973ad54651be4bf8728ba81adcb9a1d3ce3820cea24f2c6bfdde427edb7706dfd1e8e50c0ab544022275fd457fbd57555cfc85bbfbed6eb0f7a8ea04ebc1b40fc8d3f67402a01d169ed1448ed2e708e16a0fe2c1ab5055be446594036b5ecae456486addbebbd09ca3423d4c15e2afce6b40b545bfbf5859b6bf913e80bcec00e88cba671fee9770fdace16301fbb23b0eca85549ae0be1a96bda5167b6e5fb837edecd981b61e06858badb34ead1e93746ab5afd2bc10e95bffc4dc965de242db63eb8ec221cf57e2c77241fcfca70818a85a447b6fbcaaf4b2fc2135565cbfc03804aa11ad0ab0582e5d86590df2ecfd561dc6e1cdb08d3e10901312326a45fb0498a177319389809c6ba07a76cfad621e07b9af097730e94df92fbd311b2cb5da32c80ab5f14971b6d40f8e2ab202ac98bd8439790764a40bf309ea2205c1632610956495720030a25dc7118e0c868fdfa78c3e9ecce58215579a0581b3bafdb7dbbe53be9e904567fdc0ce1236aab5d22f1ebc18997e3ea83d362d891e04c5785fd5238326f767bce499209f8db211a50e1402160486e98e7235cf397dbb9ae19fd9b79ef589c821c6f99f28be33452405a003b33f4540fe0a41dfcc286f4d7cc10b70552ba7850869abadcd4bb7f256823face853633d6e2a999ac9fcd259c71d08e266db5d744e1909a62c0db673745ad9585949d108ab96640d2bc27fb4acac7fa8b170a30055a5ede90e004df9a44bdc29aeb4a6bec1e85dde1de6aaf01c6a5d12405d0bec22f49026cb23264f8c04b8401d3c2ab6f2e109948b6193b3bec27adfe19fb8afb8a92364d6fc5b219e8737d583e7ff3a4bcb75d53edda3bf3f52896ac36d8a877ad9f296ea6c045603fc62ac4ae41272bde85ef7c3b3fd3538aacfd5b025fefbe277c2906821ecb20e6f75ea479fa3280f9100fb0089203455c56b6bc775e5c2f0f58c63edd63fa3eec0b40da4b276d0d41da2ec0ead865a98d12bc694e23d8eaadd2b4d0ee88e9570c88fb878930f492e036d27998d593e47763927ff7eb80b188864a3846dd2238f7f95f4090ed399ae95deaeb37abca1cf37c397cc12189affb42dca46b4ff6988eb8c060691d155302d448f50ff70a794d97c0408f8cee9385d6a71fa412e36edcb22dbf433db9db4779f27b682ee17fc05e70c8e794b9f7f6d1
+
+fulfillment payload for node 0:
+8c0d9ee20671d1cdca98bb4c8dd5d490105b63021afcb90b22f33f1d9d6b7faafb86ec57ec4b56ad11e122bb0289403447a0b814ef8aefb90d2b0c3567501eb3570011ee96514db6050747a1d5cba50fe6e0c3e639d8208c549d61a502eab8772eceafeee940a586fc0ad92ce57773e307a5055604b9ea68c780617d6a797467d357d185fe9eb4279719d35faaab1105ab77299bd10f3504c439058720ed738a1c16756a44ba348bb193ec7d3dd9f11ac3d0cc1277ace098ed0ed8e25a01e83216a9c6a183a2659ddf2d971bc4a9b5ce776e1b07242a89a15026a3d208bbb8e3f91086f2785be68b5e4122f9d79583b883266ba6dfb4cf0c22737915dda898e1461651818c17f811f51f496f0842223c
+attribution data for node 0:
+84986c936d26bfd3bb2d34d3ec62cfdb63e0032fdb3d9d75f3e5d456f73dffa7e35aab1db4f1bd3b98ff585caf004f656c51037a3f4e810d275f3f6aea0c8e3a125ebee5f374b6440bcb9bb2955ebf706a8fd90a3bef40add4106d8c215290aafbc30881b17d68b6a7aac1b6ab52261df2fceef75d3a490862d4bab3aa510cfb8ca695b38c640b58bb8685db8bb57f93db49657ddda39d21d6074d9ecaa1ea0da6cc904e136dbb4c1ba9839a7fb8367c88662f7f573c82c2746d122cca96b2e7872a48e32c8adba45babdc57a9bca55720b1d2c922622fcf8f8a5ed7daae286484b701e69bd40554a4cc3418444ff7eb9982ecee020c8a92717f089ce1ab0ca00bb3a582253cd832a2f869703a2fadafd8261ebb93fde2a9f4f13dc8470436c3cd110bdcc7f5a1e9f9c743c8e55b50c45dac07e07a8e5c7416de895940fde032786594bbaa8a1147422bd5e926fea510408c249fb61164bf7bdbac53d9fb9704023314c332a865146893ad99743708c4a34a30a82fd8b756c561a6520d47b2cd487299a96322dadc463ef06127fc63902ff9cc4f265e2fbd9de3fa5e48b7b51aa0850580ef9f3b5ebb60c6c3216c5a75a93e82936113d9cad57ae4a94dd6481954a9bd1b5cff4ab29ca221fa2bf9b28a362c9661206f896fc7cec563fb80aa5eaccb26c09fa4ef7a981e63028a9c4dac12f82ccb5bea090d56bbb1a4c431e315d9a169299224a8dbd099fb67ea61dfc604edf8a18ee742550b636836bb552dabb28820221bf8546331f32b0c143c1c89310c4fa2e1e0e895ce1a1eb0f43278fdb528131a3e32bfffe0c6de9006418f5309cba773ca38b6ad8507cc59445ccc0257506ebc16a4c01d4cd97e03fcf7a2049fea0db28447858f73b8e9fe98b391b136c9dc510288630a1f0af93b26a8891b857bfe4b818af99a1e011e6dbaa53982d29cf74ae7dffef45545279f19931708ed3eede5e82280eab908e8eb80abff3f1f023ab66869297b40da8496861dc455ac3abe1efa8a6f9e2c4eda48025d43a486a3f26f269743eaa30d6f0e1f48db6287751358a41f5b07aee0f098862e3493731fe2697acce734f004907c6f11eef189424fee52cd30ad708707eaf2e441f52bcf3d0c5440c1742458653c0c8a27b5ade784d9e09c8b47f1671901a29360e7e5e94946b9c75752a1a8d599d2a3e14ac81b84d42115cd688c8383a64fc6e7e1dc5568bb4837358ebe63207a4067af66b2027ad2ce8fb7ae3a452d40723a51fdf9f9c9913e8029a222cf81d12ad41e58860d75deb6de30ad
+
# References
[sphinx]: http://www.cypherpunks.ca/~iang/pubs/Sphinx_Oakland09.pdf