Skip to content

fuzz: add force-close support to chanmon_consistency#4381

Draft
joostjager wants to merge 14 commits intolightningdevkit:mainfrom
joostjager:fuzz-force-close
Draft

fuzz: add force-close support to chanmon_consistency#4381
joostjager wants to merge 14 commits intolightningdevkit:mainfrom
joostjager:fuzz-force-close

Conversation

@joostjager
Copy link
Copy Markdown
Contributor

@joostjager joostjager commented Feb 4, 2026

Add force-close coverage to the chanmon_consistency fuzzer. Previously, the fuzzer only exercised cooperative channel flows. This PR enables the fuzzer to force-close channels and verify that on-chain resolution, HTLC timeouts, and payment preimage propagation all work correctly under channel monitor consistency
constraints.

@ldk-reviews-bot
Copy link
Copy Markdown

ldk-reviews-bot commented Feb 4, 2026

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

@codecov
Copy link
Copy Markdown

codecov bot commented Feb 10, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.06%. Comparing base (423c1dc) to head (ba6cbfa).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4381      +/-   ##
==========================================
+ Coverage   87.00%   87.06%   +0.06%     
==========================================
  Files         163      161       -2     
  Lines      109002   108992      -10     
  Branches   109002   108992      -10     
==========================================
+ Hits        94839    94897      +58     
+ Misses      11678    11611      -67     
+ Partials     2485     2484       -1     
Flag Coverage Δ
fuzzing ?
fuzzing-fake-hashes 30.80% <ø> (?)
fuzzing-real-hashes 29.11% <ø> (?)
tests 86.12% <ø> (+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.

Comment thread fuzz/src/chanmon_consistency.rs Outdated
},
events::Event::SplicePending { .. } => {},
events::Event::SpliceFailed { .. } => {},
events::Event::ChannelClosed { .. } => {},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should probably open a new channel to replace the force closed one?

Comment thread fuzz/src/chanmon_consistency.rs Outdated

// Only check for no broadcasts if no force-closes happened.
if !fc_ab && !fc_bc {
assert!(broadcast.txn_broadcasted.borrow().is_empty());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I have some changes that will be going up soon that rework this, you may want to wait until then. Each node will have its own broadcaster, and there's also a concept of a "chain" now so we can mine transactions.

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.

Changes were very useful! The per-node broadcasters (broadcast_a/broadcast_b/broadcast_c) are used to selectively drain and confirm each node's force-close commitment txs, and the ChainState abstraction is used to confirm broadcast transactions and advance block height past HTLC timelocks during settlement.

@TheBlueMatt
Copy link
Copy Markdown
Collaborator

Needs rebase. Is this stalled waiting on fixes that were discovered by the fuzzer?

@joostjager
Copy link
Copy Markdown
Contributor Author

I was working on my local branch only for a while. Just pushed what I have. But indeed, the mixed mode failure is also showing up in different ways with fc fuzzing.

Move shared fuzz logic into the root fuzz crate and generate
fake-hashes and real-hashes runner crates.

Keep `chanmon_consistency_target` on the real-hashes side, remove
the fuzz-local Cargo config, and update scripts, CI, coverage,
and docs to use explicit flags for each runner.

Generate the hash-mode compile checks in the wrapper bins
without a synthetic Cargo feature, while keeping the wrapper
template close to its original shape.

AI tools were used in preparing this commit.
Store real payment preimages in `chanmon_consistency` and use
them when claiming funds, so the real-hashes runner does not
treat `payment_hash` bytes as a stand-in preimage.

AI tools were used in preparing this commit.
Track in-progress monitor snapshots through restarts
and settle claimed payments according to the
force-close dust outcome on used paths.

Fix the P2WPKH witness estimate for HTLC bump fee
checks and replace the ad hoc crash corpus with
named representative regressions.

AI tools were used in preparing this commit.
Allow claimed payments on dust-touched force-close paths to end
in either PaymentSent or PaymentFailed, while still requiring
PaymentClaimed and a sender terminal outcome.

Update FC-INFO and OPEN-ISSUES to match the new invariant and
the remaining targeted repro set.

Verified with rl-tools fuzz runner:
- run-1776524390: 8 ok, 0 failed
- run-1776524663: 1 ok, 5 failed
Move monitor snapshot tracking into HarnessPersister.
Remove the TestChainMonitor wrapper. Restarts and settle
paths now drive the real ChainMonitor directly, while the
persister stays the source of truth for in-flight monitor
snapshots.

Verified with ~/repo/rl-tools/run_fuzz_runner.sh
--timeout-secs 20: 383 ok, 5 failed, 0 timed out.
The remaining failures are the known contentious-claim and
duplicate-claim families.

AI tools were used in preparing this commit.
Prevent duplicate delayed-claim replays from registering the
same ClaimId, and remember irreversibly spent outpoints so
later preimages cannot resurrect impossible claims.

Update the force-close issue notes and add a detailed
OnchainTxHandler bug writeup with raw fuzz repro bytes and
verification runs.

Verified with ~/repo/rl-tools/run_fuzz_runner.sh --timeout-secs
20:
- targeted duplicate-family rerun: 2 ok, 0 failed, 0 timed
  out
- targeted contentious-family rerun: 3 ok, 0 failed, 0 timed
  out
- full corpus rerun: 388 ok, 0 failed, 0 timed out

AI tools were used in preparing this commit.
The harness kept older pending monitor blobs after a later
update completed. Restart selectors could then reload a
stale monitor even though the serialized ChannelManager no
longer advertised blocked updates for it, causing
DangerousValue.

Retire every pending monitor blob at or below the completed
update id, keep two representative regressions, and update
OPEN-ISSUES.

Verified with:
run-1776586676: 2 ok / 0 failed / 0 timed_out
run-1776585500: 396 ok / 2 failed / 0 timed_out

AI tools were used in preparing this commit.
Replace duplicate pending claim events by ClaimId.

This aligns the initial enqueue path with the keyed rebroadcast,
bump, and reorg paths.

Add two repro cases and update the issue notes.

Fuzz runner:
run-1776587199, 2 ok / 0 failed
run-1776587008, 392 ok / 0 failed

AI tools were used in preparing this commit.
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.

4 participants