Skip to content

htlcswitch: add link state machine fuzz harness#1

Closed
MPins wants to merge 11 commits into
masterfrom
link_fsm_fuzz
Closed

htlcswitch: add link state machine fuzz harness#1
MPins wants to merge 11 commits into
masterfrom
link_fsm_fuzz

Conversation

@MPins

@MPins MPins commented Mar 12, 2026

Copy link
Copy Markdown
Owner

This PR adds a coverage-guided fuzz harness that exercises the link state machine by randomly interleaving HTLC additions, commits, revocations, settlements, failures, link restarts, and transitions into and out of quiescence mode from both Alice and Bob.

Making each iteration cheap enough to fuzz

A coverage-guided fuzzer typically needs to execute hundreds of iterations per second to be able to find interesting states. The dominant per-iteration costs in the normal link path are secp256k1 arithmetic and disk I/O, neither of which are the focus of this harness. To keep the state machine the bottleneck rather than crypto and the filesystem, the harness introduces three substitutions:

  • Mocked signing and verification
  • Mocked commitment key derivation
  • In-memory database*

*channeldb is backed by bbolt files created under t.TempDir(). To avoid the disk I/O bottleneck, the harness redirects TMPDIR to /dev/shm (tmpfs) so the bbolt files live in RAM, because tmpfs is Linux-specific, the harness fails fast on non-Linux hosts.

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

Comment thread htlcswitch/fuzz_link_test.go Outdated
Comment thread htlcswitch/link_isolated_test.go Outdated
@coveralls

coveralls commented Mar 12, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 26690713350

Coverage decreased (-0.08%) to 62.2%

Details

  • Coverage decreased (-0.08%) from the base build.
  • Patch coverage: 53 uncovered changes across 3 files (170 of 223 lines covered, 76.23%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
htlcswitch/mock.go 83 47 56.63%
htlcswitch/test_utils.go 96 85 88.54%
lnwallet/channel.go 29 23 79.31%
Total (5 files) 223 170 76.23%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 231211
Covered Lines: 143814
Line Coverage: 62.2%
Coverage Strength: 19120.11 hits per line

💛 - 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 MPins force-pushed the link_fsm_fuzz branch 10 times, most recently from fcb4bb0 to 6e4610c Compare April 1, 2026 12:59
@MPins MPins force-pushed the link_fsm_fuzz branch 2 times, most recently from 78af141 to 7a5aa99 Compare April 8, 2026 19:40
@MPins

MPins commented Apr 9, 2026

Copy link
Copy Markdown
Owner Author

@Crypt-iQ when you have time, could you take a look?

@Crypt-iQ

Crypt-iQ commented Apr 9, 2026

Copy link
Copy Markdown

@Crypt-iQ when you have time, could you take a look?

Sure I will take a look

@MPins MPins force-pushed the link_fsm_fuzz branch 4 times, most recently from b28e3d8 to 5f570d5 Compare April 11, 2026 01:34
MPins added 2 commits April 30, 2026 11:35
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 22b07c0 to 7b757e2 Compare April 30, 2026 19:16
@MPins MPins force-pushed the link_fsm_fuzz branch 2 times, most recently from 7e3c7cc to de396f6 Compare May 9, 2026 11:23
MPins added 2 commits May 9, 2026 08:31
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
Add a failReason string field to channelLink that is populated by
failf alongside the existing failed flag. This gives fuzz and unit
tests direct access to the human-readable failure reason without
requiring a dedicated OnChannelFailure callback or log scraping.
@MPins MPins force-pushed the link_fsm_fuzz branch from de396f6 to 21e9fce Compare May 9, 2026 11:39
Introduce a one-shot nextOnionFailMode flag on mockIteratorDecoder
and matching payloadFail / extractFail fields on mockHopIterator so
that fuzz and unit tests can deterministically exercise the three
error branches of channelLink.processRemoteAdds:

  - onionFailDecode  → DecodeHopIterator returns a non-CodeNone
                        failcode (CodeTemporaryChannelFailure).
  - onionFailPayload → HopPayload returns hop.ErrInvalidPayload.
  - onionFailExtract → ExtractErrorEncrypter returns a non-CodeNone
                        failcode (CodeInvalidOnionVersion).

The flag is consumed and cleared on each DecodeHopIterator call so
it affects exactly one HTLC. Default behaviour is unchanged when no
mode is armed, so existing callers see no difference.

newMockHopIterator now returns *mockHopIterator (instead of
hop.Iterator) so the decoder can set the per-iterator failure flags
after construction; the concrete type still satisfies hop.Iterator
and the only external caller in test_utils.go is unaffected.
@MPins MPins force-pushed the link_fsm_fuzz branch 3 times, most recently from 0f1c1b3 to a5df5f9 Compare May 20, 2026 17:46
MPins added 5 commits May 29, 2026 07:26
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.
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.
createTestChannel started alicePool and bobPool but never stopped
them. During fuzzing this caused goroutines to leak per. Register
t.Cleanup handlers to call Stop() on both pools so all workers are
torn down when the test ends.
newMockRegistry started an InvoiceRegistry but never stopped it.
InvoiceRegistry internally starts two background goroutines —
invoiceEventLoop and the InvoiceExpiryWatcher mainLoop — that
run for the lifetime of the registry. Without a matching Stop()
call both goroutines leaked for every test that called
newMockRegistry, accumulating thousands of goroutines during
fuzzing.

Register a t.Cleanup to call registry.Stop() so both loops are
torn down when the test ends.
@MPins MPins changed the title htlcswitch: add FSM fuzz harness for channelLink commit protocol htlcswitch: add link state machine fuzz harness May 30, 2026
@MPins MPins force-pushed the link_fsm_fuzz branch 2 times, most recently from f58c5a1 to 5204c28 Compare May 30, 2026 17:47
@MPins

MPins commented Jun 8, 2026

Copy link
Copy Markdown
Owner Author

Superseded by upstream PR lightningnetwork#10865. Further review and development will continue in the upstream pull request.

@MPins MPins closed this Jun 8, 2026
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