Skip to content

fuzz: remove macros from chanmon_consistency#4571

Open
joostjager wants to merge 15 commits intolightningdevkit:mainfrom
joostjager:fuzz-no-macros
Open

fuzz: remove macros from chanmon_consistency#4571
joostjager wants to merge 15 commits intolightningdevkit:mainfrom
joostjager:fuzz-no-macros

Conversation

@joostjager
Copy link
Copy Markdown
Contributor

This PR removes the macro-heavy structure from chanmon_consistency.rs and rewrites it as explicit Rust code.

The main reason is compile time. The macros in this harness slow builds down enough that it becomes very noticeable during iteration. In follow-up force-close fuzzing work, the chanmon_consistency build time increased to around 5 minutes on my machine. That is too expensive for a fuzz target that needs frequent rebuilds.

The macros also make the file harder to read and reason about. Replacing them with normal types and methods should make the harness easier to maintain while also reducing the macro expansion cost.

Builds on #4565

@ldk-reviews-bot
Copy link
Copy Markdown

ldk-reviews-bot commented Apr 20, 2026

👋 I see @valentinewallace was un-assigned.
If you'd like another reviewer assignment, please click here.

@joostjager
Copy link
Copy Markdown
Contributor Author

Pushed result of experimental automatic commit splitting using a stop hook.

@joostjager joostjager force-pushed the fuzz-no-macros branch 4 times, most recently from 5209d09 to 6d3d4e6 Compare April 28, 2026 10:42
Extract the repeated peer-connection and channel-funding setup into
small helpers. This leaves the fuzz scenario setup behavior unchanged
while making later harness refactors easier to review.
Introduce a small wrapper around each channel manager and its test
resources. This keeps node-local state together before moving more
operations onto the harness.
Move construction of loggers, keys, monitors, broadcasters, wallets,
and fee estimators into node resource setup. This removes ad hoc local
closures while preserving the deterministic test inputs used by the
fuzzer.
Centralize creation of the three chanmon harness nodes. The fuzzer now
initializes the node array through one path, which reduces duplicated
setup before the event and payment helpers are split out.
@joostjager joostjager force-pushed the fuzz-no-macros branch 2 times, most recently from f370956 to 4e3cce7 Compare April 28, 2026 11:26
@joostjager
Copy link
Copy Markdown
Contributor Author

joostjager commented Apr 28, 2026

Rebased, cleaned up auto-splitted commits, minimized diffs. Currently fuzz CI fails and seems to be a regression.

Move persistence, reload, and chain sync state onto each harness node.
Keeping serialized managers and heights with the node makes restarts and
block updates easier to reason about.
Lift monitor update, splice, and chain sync actions into named helper
functions. This keeps the byte-dispatch loop focused on choosing actions
rather than spelling out each operation.
Move the action helpers onto `HarnessNode` methods. Node-local
operations now live with the state they mutate, which reduces argument
threading through the fuzz loop.
Replace the four directional message vectors with one queue owner.
The fuzz loop now uses that owner at send, receive, drain, and
reload sites while preserving the existing routing behavior.
Move per-node queue draining, middle-node routing, and disconnect
cleanup into EventQueues. The fuzz loop now asks the queue owner to
route remaining messages instead of mutating each directional vector
directly.
Pull message-event delivery into standalone helpers. This keeps the fuzz
dispatch loop smaller while preserving the same corruption and
one-message processing modes.
Represent each channel pair as a peer link with its channel ids and
disconnect state. Link methods now own peer reconnect, disconnect, and
monitor-update operations for that channel group.
Move payment bookkeeping into a payment tracker. Sending, resolving,
claiming, and stuck-payment assertions now share one state owner instead
of borrowing several local maps.
Collect the node, link, queue, chain, and payment setup into a harness
builder. This keeps the initial fuzz scenario construction together and
leaves the action loop with a smaller state surface.
Wrap the chanmon consistency state in a `Harness` struct. The fuzz loop
now accesses nodes, links, queues, payments, and chain state through one
owner while keeping the existing byte actions intact.
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.08%. Comparing base (df49980) to head (66dc37b).
⚠️ Report is 9 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4571      +/-   ##
==========================================
- Coverage   87.10%   87.08%   -0.03%     
==========================================
  Files         161      161              
  Lines      109246   109246              
  Branches   109246   109246              
==========================================
- Hits        95162    95132      -30     
- Misses      11600    11630      +30     
  Partials     2484     2484              
Flag Coverage Δ
fuzzing-fake-hashes 31.12% <ø> (+<0.01%) ⬆️
fuzzing-real-hashes 22.84% <ø> (+0.02%) ⬆️
tests 86.14% <ø> (-0.03%) ⬇️

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 April 29, 2026 06:32
Comment thread fuzz/src/chanmon_consistency.rs
Comment thread fuzz/src/chanmon_consistency.rs
);
for (id, data) in state.pending_monitors.drain(..) {
self.monitor.chain_monitor.channel_monitor_updated(*chan_id, id).unwrap();
if id > state.persisted_monitor_id {
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.

Nit: This uses > (strict) while complete_all_pending_monitor_updates at line 1083 uses >=. When watch_channel returns InProgress, persisted_monitor_id is set equal to the pending monitor's id (and persisted_monitor is empty Vec::new()). If this method were ever called on a channel whose initial watch_channel InProgress entry hasn't been completed yet, > would skip updating persisted_monitor, leaving it as an empty vec.

In practice this path isn't reachable today because make_channel always calls complete_all_pending_monitor_updates (with >=) first. But using >= here (matching the other method) would be safer and less surprising for future maintenance.

Same applies to complete_monitor_update at line 1116.

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.

Leaving as is to keep this PR a strict refactor.

@ldk-claude-review-bot
Copy link
Copy Markdown
Collaborator

ldk-claude-review-bot commented Apr 29, 2026

Good. complete_all_pending_monitor_updates (with >=) is only used in make_channel during setup. The opcode handlers (0x08-0x0b) and process_all_events both use complete_all_monitor_updates (with >). My previously-posted inline comments already flag both issues. No new issues found on this pass.

Review Summary

Previously reported issues (still unresolved):

  1. fuzz/src/chanmon_consistency.rs:1059-1061Bug: set_persistence_style does not propagate to the live TestPersister::update_ret, silently neutering fuzz coverage for InProgress <-> Completed persistence transitions (opcodes 0x00-0x06). The old code updated both mon_style[N] and monitor_N.persister.update_ret directly.

  2. fuzz/src/chanmon_consistency.rs:1071Inconsistency: complete_all_monitor_updates (per-channel) uses id > while complete_all_pending_monitor_updates (all channels) uses id >=. Same inconsistency at line 1116 in complete_monitor_update. Not a practical bug given current call patterns, but a latent hazard if the initial watch_channel InProgress entry hasn't been completed.

No new issues found on this re-review pass. The rest of the refactoring faithfully reproduces the original behavior:

  • EventQueues routing and route_from_middle correctly replicate the old push_excess_b_events! macro.
  • process_msg_events_impl and process_events_impl faithfully reproduce the old processing loops.
  • make_channel, lock_fundings, connect_peers, reload all match original semantics.
  • disconnect / disconnect_for_reload / reconnect correctly handle event draining and queue clearing.
  • By-value iteration on update_fulfill_htlcs (line 2462) is correct — handle_update_fulfill_htlc takes msg: UpdateFulfillHTLC by value.
  • Splice operations, fee bumping, signer ops, and payment tracking all preserve original semantics.

@joostjager joostjager removed the request for review from valentinewallace April 29, 2026 06:52
@joostjager
Copy link
Copy Markdown
Contributor Author

joostjager commented Apr 29, 2026

Verified coverage in codecov. It is equal to main. Still reaching the same code paths after the refactor.

Move the main fuzz flow onto the harness. This completes the structural
refactor so `do_test` is responsible for constructing and running the
harness rather than managing the full scenario directly.

Keep the final event-settling loop on the harness as well,
avoiding an extra free function once the state it needs is already
owned by `Harness`.
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