Skip to content

Make fuzz targets deterministic#4525

Open
joostjager wants to merge 1 commit intolightningdevkit:mainfrom
joostjager:fuzz-deterministic
Open

Make fuzz targets deterministic#4525
joostjager wants to merge 1 commit intolightningdevkit:mainfrom
joostjager:fuzz-deterministic

Conversation

@joostjager
Copy link
Copy Markdown
Contributor

@joostjager joostjager commented Mar 31, 2026

Use deterministic time and hash table ordering when building with cfg(fuzzing) to ensure fuzz test cases reproduce consistently.

@ldk-reviews-bot
Copy link
Copy Markdown

ldk-reviews-bot commented Mar 31, 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.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 31, 2026

Codecov Report

❌ Patch coverage is 60.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.10%. Comparing base (450c03a) to head (bb4d148).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
lightning/src/ln/channelmanager.rs 0.00% 1 Missing ⚠️
lightning/src/util/hash_tables.rs 75.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4525      +/-   ##
==========================================
- Coverage   87.10%   87.10%   -0.01%     
==========================================
  Files         163      163              
  Lines      108740   108748       +8     
  Branches   108740   108748       +8     
==========================================
+ Hits        94723    94729       +6     
- Misses      11531    11533       +2     
  Partials     2486     2486              
Flag Coverage Δ
fuzzing 40.24% <20.00%> (+0.04%) ⬆️
tests 86.19% <75.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@joostjager joostjager marked this pull request as ready for review March 31, 2026 09:13
@joostjager joostjager requested a review from TheBlueMatt March 31, 2026 09:13
@ldk-claude-review-bot
Copy link
Copy Markdown
Collaborator

ldk-claude-review-bot commented Mar 31, 2026

Review Summary

Inline comments posted

  • lightning/src/routing/gossip.rs:2323 — Split #[cfg(not(fuzzing))] is 17 lines away from the existing #[cfg(feature = "std")] on line 2306; should be combined into one #[cfg(all(feature = "std", not(fuzzing)))] for consistency with the rest of the PR.

Prior review issues (not repeated)

The cross-cutting concerns from my prior review still stand:

  1. Incomplete SystemTime::now() coverage — Several SystemTime::now() calls reachable from fuzz targets remain gated on just #[cfg(feature = "std")] without not(fuzzing), particularly in routing/gossip.rs (channel_failed_permanent, node_failed_permanent, add_channel_announcement_internal, update_channel_internal).
  2. channelmanager.rs:~15000 — 3-branch pattern inconsistent with the 2-branch pattern used at other locations.
  3. fuzz/Cargo.toml feature unification — Transitive dependencies re-enable lightning's default features.

@joostjager
Copy link
Copy Markdown
Contributor Author

Claude comments addressed

#[cfg(any(not(feature = "std"), fuzzing))]
let now = Duration::from_secs(self.highest_seen_timestamp.load(Ordering::Acquire) as u64);
#[cfg(feature = "std")]
#[cfg(all(feature = "std", not(fuzzing)))]
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.

Shouldn't we just fuzz with no-std, then?

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.

We'd redefine no-std as being deterministic, seems like an undesirable shortcut.

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.

I meant just for the time logic - no-std is already defined as not supporting fetching the current time, seems reasonable to use that.

Copy link
Copy Markdown
Contributor Author

@joostjager joostjager Apr 1, 2026

Choose a reason for hiding this comment

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

It appears that deterministic hashes were already in the code for fuzzing, so the only change needed is then to switch to no std.

Claude review pointed out that no-std wasn't actually enabled. And enabling it runs into the issue that test_utils also needs to be made no-std compatible. Then it becomes a larger change.

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 am still in favor of the original change that adds the fuzzing flag to the time code.

@joostjager joostjager force-pushed the fuzz-deterministic branch from 1a81a3a to 9ffd8b3 Compare April 1, 2026 07:20
fuzz/Cargo.toml Outdated

[dependencies]
lightning = { path = "../lightning", features = ["regex", "_test_utils"] }
lightning = { path = "../lightning", default-features = false, features = ["regex", "_test_utils"] }
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.

Bug: This change is a no-op due to Cargo feature unification.

lightning-persister on line 25 is included without default-features = false:

lightning-persister = { path = "../lightning-persister", features = ["tokio"]}

And lightning-persister/Cargo.toml depends on lightning with default features:

lightning = { version = "0.3.0", path = "../lightning" }

Lightning's defaults are ["std", "grind_signatures"]. Through Cargo feature unification, lightning-persister's dependency re-enables both std and grind_signatures on the unified lightning crate, making your default-features = false here have no effect.

Similarly, lightning-liquidity (line 23) uses default features which include std = ["lightning/std"], also re-enabling lightning/std.

To actually disable grind_signatures, you'd need to ensure no transitive dependency pulls in lightning with default features. At minimum, lightning-persister/Cargo.toml itself would need to be updated to use default-features = false for its lightning dependency (and explicitly enable only the features it needs, likely std).

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.

Good point...

@joostjager joostjager force-pushed the fuzz-deterministic branch from 9ffd8b3 to 1a81a3a Compare April 1, 2026 07:52
Gate all SystemTime::now() and Instant::now() calls in production code
with #[cfg(all(feature = "std", not(fuzzing)))] so that fuzz targets
produce consistent results regardless of wall-clock time. For each
location, the existing no-std fallback (highest_seen_timestamp, None,
or a constant) is reused under fuzzing.

Also force deterministic hashing when the fuzzing cfg is active,
rather than requiring the LDK_TEST_DETERMINISTIC_HASHES env var.

AI tools were used in preparing this commit.
@joostjager joostjager force-pushed the fuzz-deterministic branch from 1a81a3a to 9f4e929 Compare April 1, 2026 08:29
/// This method is only available with the `std` feature. See
/// This method is only available with the `std` feature (and not during fuzzing). See
/// [`NetworkGraph::remove_stale_channels_and_tracking_with_time`] for non-`std` use.
#[cfg(not(fuzzing))]
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.

The new #[cfg(not(fuzzing))] here is split from the pre-existing #[cfg(feature = "std")] on line 2306, 17 lines above (separated by the entire doc comment). In Rust, multiple #[cfg] attributes are ANDed, so the effective gate is all(feature = "std", not(fuzzing)) — which is correct. But every other location in this PR uses a single combined attribute like #[cfg(all(feature = "std", not(fuzzing)))].

Having the two conditions split across 17 lines makes it easy to overlook one when auditing. Consider merging them into one attribute and moving it closer to the function:

Suggested change
#[cfg(not(fuzzing))]
#[cfg(all(feature = "std", not(fuzzing)))]

(and removing the #[cfg(feature = "std")] on line 2306 above the doc comment)

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.

4 participants