Skip to content

Clarify fulfillment payload padding requirement - #1353

Open
joostjager wants to merge 3 commits into
lightning:masterfrom
joostjager:clarify-fulfillment-payload-padding
Open

joostjager wants to merge 3 commits into
lightning:masterfrom
joostjager:clarify-fulfillment-payload-padding

Conversation

@joostjager

Copy link
Copy Markdown
Collaborator

Require a padding record only when the serialized TLV stream does not already meet the 256-byte bucket requirement.

Require a padding record only when the serialized TLV stream does
not already meet the 256-byte bucket requirement.
@t-bast

t-bast commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

I thought you were also talking about the padding done for failure messages, since your comment didn't include an exact link to the spec. We have weird repeated requirements where the first one says MUST and the second one says SHOULD here: https://github.com/lightning/bolts/blob/master/04-onion-routing.md#requirements-4

That's why I suggested to open a follow-up PR, I didn't understand that your comment was explicitly scoped to fulfillment payload. Probably worth taking a look at every place where we pad in a similar way?

@joostjager

Copy link
Copy Markdown
Collaborator Author

I think the MUST+SHOULD is a result of lnd not able to parse 256+ byte failure reasons in the past. Maybe we can just drop that now?

But yes, my comment was scoped just to the fulfill payload. It's not that the spec is wrong as is now, but it also isn't completely clear the padding tlv can be omitted.

@t-bast

t-bast commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

But yes, my comment was scoped just to the fulfill payload.

Sorry then, I was too trigger-happy when I merged that PR, I didn't understand what you meant!

I think the MUST+SHOULD is a result of lnd not able to parse 256+ byte failure reasons in the past. Maybe we can just drop that now?

Yes I think that since we're doing a dedicated PR and it has been quite some time, it's a good opportunity to clean that up now.

Remove the recommendation to keep padded failure messages at exactly
256 bytes now that senders support longer failure messages.
@joostjager

Copy link
Copy Markdown
Collaborator Author

Updated

@joostjager

Copy link
Copy Markdown
Collaborator Author

One unfortunate thing about padding to 256 bytes using only TLV type 1 (padding) is that it is impossible for otherwise-empty fulfillment payloads. The BigSize length transition skips from a 254-byte record to a 257-byte record, so the next valid bucket is 512 bytes, or 528 bytes including the Poly1305 tag 😞

@t-bast t-bast mentioned this pull request Aug 27, 2026
15 of 22 tasks
@joostjager

Copy link
Copy Markdown
Collaborator Author

Not sure if I am still all that happy with the in-tlv padding

@t-bast

t-bast commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

One unfortunate thing about padding to 256 bytes using only TLV type 1 (padding) is that it is impossible for otherwise-empty fulfillment payloads.

Why do you think a present but empty padding TLV makes sense (if I understand your scenario correctly - please be more precise if I'm misunderstanding)? Can you detail that scenario? It looks like an edge case that we simply will never reach, doesn't it?

Comment thread 04-onion-routing.md
@@ -1138,9 +1138,6 @@ which is in turn applied to the `attribution_data` field using `XOR`.
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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do we want to take this opportunity to use multiples of 256 bytes here as well for consistency?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It can't be a MUST. I could re-add a new SHOULD? 😅

In this touch-up PR I wouldn't want to change anything really, just clarify.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It is on the sender requirements, so it can definitely be a MUST as long as we don't change the receiver requirements yet? But we can indeed skip it, maybe worth checking with lnd and cln whether they think it makes sense to do.

@joostjager

Copy link
Copy Markdown
Collaborator Author

Why do you think a present but empty padding TLV makes sense (if I understand your scenario correctly - please be more precise if I'm misunderstanding)? Can you detail that scenario? It looks like an edge case that we simply will never reach, doesn't it?

I was thinking of an otherwise-empty payload as cover traffic, since omitting fulfillment_payload may reveal to upstream nodes whether the recipient returned application data.

@t-bast

t-bast commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

I was thinking of an otherwise-empty payload as cover traffic, since omitting fulfillment_payload may reveal to upstream nodes whether the recipient returned application data.

Can't you just include an all-0 fulfillment payload then instead? Or any other unrelated "cover traffic" TLV if necessary?

@joostjager

Copy link
Copy Markdown
Collaborator Author

Can't you just include an all-0 fulfillment payload then instead? Or any other unrelated "cover traffic" TLV if necessary?

A literal all-zero fulfillment_payload would not carry a valid Poly1305 tag and would also be recognizable as cover traffic by the upstream node?

The 256-byte issue is specific to using a one-byte-encoded padding type such as type 1. The record size is 1 + BigSize(length) + length: it jumps from 254 bytes with a 252-byte value to 257 bytes with a 253-byte value because the length encoding expands from one to three bytes. Had we chosen a padding type of at least 253, its three-byte type encoding plus a one-byte length and 252-byte value could produce exactly 256 bytes.

We could define a separate cover TLV, but having both padding and cover fields in the specification seems redundant?

@Roasbeef

Copy link
Copy Markdown
Collaborator

Can we add some test vectors here?

Add standalone TLV stream vectors covering omitted and empty padding
records, along with BigSize boundaries that require a larger 256-byte
multiple. Link the vectors from the BOLT 4 returning success examples.
@joostjager

Copy link
Copy Markdown
Collaborator Author

Added test vectors. They show the limitation of type-1 TLV padding pretty clearly: some 256-byte boundaries can't be reached. Not sure whether we still want to change anything about that or just roll with what we have.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants