Skip to content

Speed up TLV serialization length accounting#4642

Open
joostjager wants to merge 3 commits into
lightningdevkit:mainfrom
joostjager:ser-tlv-length-fastpath
Open

Speed up TLV serialization length accounting#4642
joostjager wants to merge 3 commits into
lightningdevkit:mainfrom
joostjager:ser-tlv-length-fastpath

Conversation

@joostjager
Copy link
Copy Markdown
Contributor

Recent splice fuzzing coverage made it clear that some chanmon consistency inputs spend a surprising amount of time encoding snapshots. This PR tightens the serialization length path used by those snapshots.

Slow fuzz byte string used while checking this:

a1a0a3ffa2a1ffffa2a1ff00a2a1ffa2a1a0a3ffa2a1ff19ffffffffffffffac

For that byte string, median real time over 5 runs with target output suppressed:

Profile Pre changes Post changes
Debug 17.17s 3.61s
Release 0.60s 0.24s

@ldk-reviews-bot
Copy link
Copy Markdown

ldk-reviews-bot commented May 28, 2026

👋 Thanks for assigning @TheBlueMatt as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@joostjager joostjager requested a review from wpaulino May 28, 2026 14:28
@joostjager joostjager marked this pull request as ready for review May 28, 2026 14:28
@ldk-claude-review-bot
Copy link
Copy Markdown
Collaborator

ldk-claude-review-bot commented May 28, 2026

I've completed a thorough re-review of all changes in this PR, including the truncated portions of the diff. I verified:

  1. _tlv_fields_serialized_length! macro (ser_macros.rs:844-859): Correctly computes len + BigSize(len).serialized_length(), matching the _encode_varint_length_prefixed_tlv! write format.

  2. impl_writeable_tlv_based! (new explicit-self variant) (ser_macros.rs:874-892): Used only for ChannelTransactionParameters, CommitmentTransaction, and FundingScope. Both write and serialized_length apply & to field expressions consistently via write_tlv_fields! and _tlv_fields_serialized_length! respectively.

  3. impl_ser_tlv_based! (ser_macros.rs:1138-1162): The serialized_length path passes ($type, self.$field, $fieldty, self) to _tlv_fields_serialized_length!, which adds & to self.$field. Write path passes ($type, &self.$field, $fieldty, self) to _encode_varint_length_prefixed_tlv!. Both paths result in &self.$field reaching the inner macros. For legacy/custom field types, $self ident is correctly forwarded. Verified against real usage in ConfirmedUtxo, ExpandedField, ChannelDetails, HTLCFailReasonRepr, etc.

  4. Option<T>::serialized_length (ser.rs:1403-1412): None → 1 byte (matching 0u8.write()). Some(data)BigSize(data_len + 1).serialized_length() + data_len (matching BigSize(data_len + 1).write() + data.write()).

  5. WithoutLength<S>::serialized_length (ser.rs:856-858): Iterates elements calling serialized_length(), matching the write path. AsWriteableSlice impls exist for &Vec<T>, &&Vec<T>, and &[T], covering all call sites.

  6. &T and Box<T> serialized_length overrides: Both delegate to inner type, matching their write implementations.

  7. Safety net: The debug_assert_eq!(len, msg.0.len()) in encode() (ser.rs:289) validates that any serialized_length() override matches actual write() output.

  8. Inline expression double-evaluation (e.g., legacy_deserialization_prevention_marker_for_channel_type_features): Was already evaluated twice in the old code (once in length calc, once in encoding inside _encode_varint_length_prefixed_tlv!). The function is pure, so this is safe.

No issues found.

@joostjager joostjager force-pushed the ser-tlv-length-fastpath branch from 7afe504 to 084d0ac Compare May 28, 2026 15:21
@joostjager joostjager self-assigned this May 28, 2026
Comment thread lightning/src/util/ser_macros.rs Outdated
@ldk-reviews-bot
Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@joostjager joostjager force-pushed the ser-tlv-length-fastpath branch from 084d0ac to b0e52bd Compare May 28, 2026 17:41
@joostjager joostjager requested a review from wpaulino May 28, 2026 18:01
Comment thread lightning/src/util/ser_macros.rs Outdated
Comment thread lightning/src/util/ser_macros.rs Outdated
Comment thread lightning/src/util/ser.rs Outdated
let mut data = Vec::with_capacity(8192);
std::io::stdin().read_to_end(&mut data).unwrap();
zbase32_test(&data, test_logger::Stdout {});
if std::env::var_os("LDK_FUZZ_SUPPRESS_LOGS").is_some() {
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.

Bleh. Tell claude env variables for runtime decisions is gross and not the clean solution it thinks it is. If the point of stdin-fuzz is to be used to replicate tests, we should leave stdout, if its purpose is as a fuzz target, it should be DevNull.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I wanted this, not Claude 😅 I use stdin_fuzz also to run big sets of test cases using a runner that parallelizes and also continues on failure to examine the rest. That wouldn't work using the cargo test and test_cases dir approach.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Open to other suggestions for a convenient way to feed strings without intermediate files, while being able to switch logging on or off.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

For the time being, moved to #4646 to unblock this PR.

@joostjager joostjager force-pushed the ser-tlv-length-fastpath branch from b0e52bd to 78817b6 Compare May 29, 2026 10:36
@joostjager joostjager requested a review from TheBlueMatt May 29, 2026 10:40
@joostjager joostjager force-pushed the ser-tlv-length-fastpath branch from 78817b6 to 2060cff Compare May 29, 2026 10:45
@joostjager
Copy link
Copy Markdown
Contributor Author

joostjager commented May 29, 2026

Review comments addressed: https://github.com/lightningdevkit/rust-lightning/compare/b0e52bd..7dd5bca

Dropped commit, so no clean diff is possible anymore.

@joostjager joostjager removed the request for review from TheBlueMatt May 29, 2026 10:48
@joostjager joostjager force-pushed the ser-tlv-length-fastpath branch 4 times, most recently from 70b69e4 to 18a137a Compare May 29, 2026 13:36
@joostjager joostjager requested a review from TheBlueMatt May 29, 2026 13:45
Comment thread lightning/src/util/ser.rs Outdated
Comment thread lightning/src/util/ser_macros.rs Outdated
/// [`Writeable`]: crate::util::ser::Writeable
/// [`impl_writeable_tlv_based`]: crate::impl_writeable_tlv_based
/// [`write_tlv_fields`]: crate::write_tlv_fields
macro_rules! impl_writeable_only_tlv_fields {
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.

Should we rename the existing macro(s) to impl_ser_tlv_based so that writeable_only_tlv_fields isn't so awkward?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Wilmer also suggested this in #4642 (comment)

The delta is quite large, but made the change.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 1st Reminder

Hey @wpaulino! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Rename TLV macros that generate both Readable and Writeable
impls to use the impl_ser_tlv_based prefix. Keep the MaybeReadable
upgradable enum helpers and shared write-only enum helper under
writeable naming so macro names match the traits they generate.
Add direct serialized length implementations for common serialization
wrappers. This avoids routing field payload length calculations through
in-memory writers for common nested serialization paths used by the
existing TLV length helpers.
Add a writeable TLV helper macro that emits both write and
serialized_length from the same field list. Reuse the shared TLV length
helper from impl_ser_tlv_based so the generated read/write path and the
new custom-read path stay aligned.

Use the new helper for the hot channel funding and commitment
transaction TLV writers while leaving their custom read implementations
unchanged.
@joostjager joostjager force-pushed the ser-tlv-length-fastpath branch from 18a137a to eb47742 Compare June 1, 2026 09:30
@joostjager joostjager requested a review from TheBlueMatt June 1, 2026 09:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

6 participants