Skip to content

fuzz-tests: Add a test for full_channel operations #8437

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

Chand-ra
Copy link

@Chand-ra Chand-ra commented Aug 5, 2025

Functions defined in channeld/full_channel.h contain channel operations like ADD_HTLC, FULFILL_HTLC, UPDATE_FEERATE, etc. Since they are a critical part of the HTLC state machine and may be influenced by external agents, add a stateful test for them.

Checklist

Before submitting the PR, ensure the following tasks are completed. If an item is not applicable to your PR, please mark it as checked:

  • The changelog has been updated in the relevant commit(s) according to the guidelines.
  • Tests have been added or modified to reflect the changes.
  • Documentation has been reviewed and updated as needed.
  • Related issues have been listed and linked, including any that this PR closes.

CC: @morehouse

Chandra Pratap added 2 commits August 5, 2025 14:48
Changelog-None: Functions defined in `channeld/full_channel.h`
contain channel operations like `ADD_HTLC`, `FULFILL_HTLC`,
`UPDATE_FEERATE`, etc.

Since they are a critical part of the HTLC state machine and may
be influenced by external agents, add a stateful test for them.
Add a minimal input set as a seed corpus for the newly introduced
test. This leads to discovery of interesting code paths faster.
Copy link
Contributor

@morehouse morehouse left a comment

Choose a reason for hiding this comment

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

This is a nice target at a high level.

I think we're missing a lot of opportunities to fuzz invalid/unexpected/rare states, where bugs could be hiding. Some ideas for improvement:

  • Unshackle the fuzzer a bit, so it can pick random values sometimes.
  • Consider allowing the fuzzer to simulate the various commitment dance messages arriving at unexpected times (e.g., sending revoke_and_ack when the peer hasn't updated the commitment yet).
  • Consider allowing the fuzzer to generate multiple HTLC operations before doing the commitment signed dance. This is valid behavior per the spec.

memset(&p_htlc.preimage, (int)p_htlc.id, sizeof(p_htlc.preimage));

if (add_htlc(lchannel, sender, p_htlc.id, &p_htlc.preimage, msat, cltv)) {
add_htlc(rchannel, sender, p_htlc.id, &p_htlc.preimage, msat, cltv);
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably should assert that the HTLC is successfully added to rchannel here.

struct pending_htlc p_htlc = pending_htlcs[idx];

if (fulfill_htlc(lchannel, p_htlc.sender, p_htlc.id, &p_htlc.preimage)) {
fulfill_htlc(rchannel, p_htlc.sender, p_htlc.id, &p_htlc.preimage);
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably should assert that the HTLC is successfully fulfilled on rchannel.


enum side sender = (fromwire_u8(&data, &size) % 2) ? REMOTE : LOCAL;
struct amount_msat msat = fromwire_amount_msat_bounded(&data, &size);
u32 cltv = current_blockheight + fromwire_u16(&data, &size);
Copy link
Contributor

Choose a reason for hiding this comment

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

This might be too restrictive. The peer could set any CLTV in update_add_htlc.

break;

enum side sender = (fromwire_u8(&data, &size) % 2) ? REMOTE : LOCAL;
struct amount_msat msat = fromwire_amount_msat_bounded(&data, &size);
Copy link
Contributor

Choose a reason for hiding this comment

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

The peer could set any amount in their update_add_htlc message.

* recv_revoke_and_ack -> recv_commit -> sending_revoke_and_ack)
* in the correct opener vs. accepter order.
*/
static void exchange_commits(struct channel *channel, enum side side, const struct htlc ***changed_htlcs)
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be very interesting to fuzz the commitment dance as well. For each of these messages, the channel peer can send all kinds of unexpected fields.

Comment on lines +159 to +160
if (!channel_get_htlc(channel, original_sender, id))
return false;
Copy link
Contributor

Choose a reason for hiding this comment

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

The peer could send an invalid HTLC ID.

}
case 3: /* UPDATE_FEE */
{
u32 feerate = fromwire_bounded_feerate(&data, &size);
Copy link
Contributor

Choose a reason for hiding this comment

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

The peer could set any feerate.

{
u32 feerate = fromwire_bounded_feerate(&data, &size);
if (update_feerate(lchannel, feerate))
update_feerate(rchannel, feerate);
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably should assert that the feerate is successfully updated for rchannel.

Comment on lines +344 to +345
u32 height_increase = 1 + fromwire_u32(&data, &size);
current_blockheight += height_increase;
Copy link
Contributor

Choose a reason for hiding this comment

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

The peer could set any blockheight, including one that is less than the previous one.

Comment on lines +354 to +357
channel_txs(tmpctx, &funding, funding_amount, &htlc_map, NULL, &funding_wscript_alt,
lchannel, &local_per_commitment_point, 42, LOCAL, 0, 0, &anchor, NULL);
channel_txs(tmpctx, &funding, funding_amount, &htlc_map, NULL, &funding_wscript_alt,
rchannel, &local_per_commitment_point, 42, REMOTE, 0, 0, &anchor, NULL);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it expensive to generate these transactions? Maybe we should generate them after each loop iteration.

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.

2 participants