Skip to content

htlcswitch: add FSM fuzz harness for channelLink commit protocol#1

Open
MPins wants to merge 7 commits intomasterfrom
link_fsm_fuzz
Open

htlcswitch: add FSM fuzz harness for channelLink commit protocol#1
MPins wants to merge 7 commits intomasterfrom
link_fsm_fuzz

Conversation

@MPins
Copy link
Copy Markdown
Owner

@MPins MPins commented Mar 12, 2026

The channelLink commit protocol — the sequence of CommitSig / RevokeAndAck exchanges that advance commitment heights on both sides of a channel — is one of the most critical and subtle state machines in lnd. Despite extensive unit tests, the ordering of these messages is highly concurrent and easy to get wrong. A single missed revocation or out-of-order commit can corrupt channel state irreparably.

This PR adds a coverage-guided fuzz harness that exercises the full commit protocol FSM by randomly interleaving HTLC additions, commits, revocations, settlements, and failures from both Alice and Bob. The fuzzer checks structural invariants (monotonic commit heights, mirror symmetry between peers) after every event, catching protocol violations that deterministic tests cannot anticipate.

Testing

go test ./htlcswitch/ -run TestChannelLinkFSMScenarios -v
go test ./htlcswitch -run=^$ -fuzz=FuzzChannelLinkFSM -fuzztime=1m

@coveralls
Copy link
Copy Markdown

coveralls commented Mar 12, 2026

Pull Request Test Coverage Report for Build 23849819738

Details

  • 147 of 191 (76.96%) changed or added relevant lines in 5 files are covered.
  • 58 unchanged lines in 14 files lost coverage.
  • Overall coverage increased (+0.05%) to 62.382%

Changes Missing Coverage Covered Lines Changed/Added Lines %
lnwallet/channel.go 30 31 96.77%
htlcswitch/test_utils.go 82 94 87.23%
htlcswitch/mock.go 20 51 39.22%
Files with Coverage Reduction New Missed Lines %
discovery/validation_barrier.go 1 86.88%
htlcswitch/switch.go 2 83.37%
input/taproot.go 2 93.48%
watchtower/wtclient/session_negotiator.go 2 80.32%
watchtower/wtdb/migration8/range_index.go 2 11.67%
lnrpc/wtclientrpc/wtclient.go 3 62.5%
lnwire/accept_channel.go 3 69.03%
lnwire/ping.go 3 80.0%
chainntnfs/interface.go 4 69.18%
routing/router.go 4 87.56%
Totals Coverage Status
Change from base Build 22996232716: 0.05%
Covered Lines: 140987
Relevant Lines: 226006

💛 - Coveralls

@MPins MPins force-pushed the link_fsm_fuzz branch 3 times, most recently from 001781c to 1400d9a Compare March 13, 2026 21:50
@MPins MPins force-pushed the link_fsm_fuzz branch 4 times, most recently from 1b219d1 to ca4be0a Compare March 28, 2026 00:16
MPins added 3 commits March 29, 2026 13:39
Expose the `invoiceRegistry` field in `singleLinkTestHarness` so
tests can register and look up invoices directly.

Add `generateSingleHopHtlc`, a test helper that builds a single-hop
`UpdateAddHTLC` with a random preimage, intended for use in unit and
fuzz tests.
Add a no-op MailBox implementation and a no-op ticker for use in
the channelLink FSM fuzz harness.
@MPins MPins force-pushed the link_fsm_fuzz branch 5 times, most recently from 29c5374 to 5fad3b6 Compare March 30, 2026 11:30
Replace createChannelLinkWithPeer (which required a Switch and spawned the
htlcManager goroutine) with newFuzzLink, a minimal link factory that:

- accepts dependencies directly (registry, preimage cache, circuit map,
  bestHeight) instead of a mockServer, so no Switch or background goroutines
  are created at all
- sets link.upstream directly to a buffered channel controlled by the
  caller, bypassing the mailbox entirely
- attaches a mockMailBox so mailBox.ResetPackets() in resumeLink succeeds
@MPins MPins force-pushed the link_fsm_fuzz branch 4 times, most recently from fa3a369 to fcb4bb0 Compare April 1, 2026 03:36
MPins added 3 commits April 1, 2026 09:52
Introduce `fuzz_link_test.go` with a model-based fuzzer that drives
the Alice-Bob channel link through arbitrary sequences of protocol
events and checks key invariants after each step.

fuzz_link_test
Introduce fuzzSigner and fuzzSigVerifier in the fuzz harness, along
with the SigVerifier hook in LightningChannel (WithSigVerifier,
verifySig) and a matching SigPool extension (VerifyFunc field) so the
harness can bypass secp256k1 verification end-to-end. Also refactors
createTestChannel to accept functional options (testChannelOpt) so
the signer and channel options can be injected from tests.
Introduce CommitKeyDeriverFunc and WithCommitKeyDeriver to allow
LightningChannel to bypass the secp256k1-based DeriveCommitmentKeys
on every commit round. All internal call sites are migrated to
lc.deriveCommitmentKeys. The fuzz harness injects fuzzCommitKeyDeriver,
a trivial identity deriver that avoids scalar-multiplication overhead.
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