Files
palladum-lightning/tests/fuzz/fuzz-funder-policy.c
Rusty Russell d8ee3a5eb9 tests/fuzz: fix include order.
Nobody ever runs `make check-includes` with fuzzing enabled.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-10-23 06:44:04 +10:30

168 lines
5.5 KiB
C

#include "config.h"
#include <ccan/array_size/array_size.h>
#include <common/setup.h>
#include <stdio.h>
#include <tests/fuzz/libfuzz.h>
#include <wire/wire.h>
#include "../../plugins/funder_policy.c"
/* AUTOGENERATED MOCKS START */
/* Generated stub for json_add_string */
void json_add_string(struct json_stream *js UNNEEDED,
const char *fieldname UNNEEDED,
const char *str TAKES UNNEEDED)
{ fprintf(stderr, "json_add_string called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */
#define MAX_SATS ((u64)WALLY_SATOSHI_PER_BTC * WALLY_BTC_MAX)
struct test_case {
struct amount_sat their_funds;
struct amount_sat available_funds;
struct amount_sat *our_last_funds;
struct amount_sat max_channel_size;
struct amount_sat lease_request;
struct funder_policy policy;
};
static struct amount_sat fromwire_amount_sat_bounded(const u8 **cursor, size_t *max)
{
struct amount_sat amt = fromwire_amount_sat(cursor, max);
amt.satoshis %= (MAX_SATS + 1); /* Raw: fuzzing */
return amt;
}
static struct test_case *new_test_case(const tal_t *ctx, const u8 **cursor, size_t *max)
{
struct test_case *tcase = tal(ctx, struct test_case);
tcase->their_funds = fromwire_amount_sat_bounded(cursor, max);
tcase->available_funds = fromwire_amount_sat_bounded(cursor, max);
/* Read flag for our_last_funds */
u8 flag = fromwire_u8(cursor, max);
/* Handle our_last_funds conditionally */
struct amount_sat *our_last_funds_val = tal(ctx, struct amount_sat);
if (flag)
{
*our_last_funds_val = fromwire_amount_sat_bounded(cursor, max);
tcase->our_last_funds = our_last_funds_val;
}
else
tcase->our_last_funds = NULL;
tcase->max_channel_size = fromwire_amount_sat_bounded(cursor, max);
tcase->lease_request = fromwire_amount_sat_bounded(cursor, max);
tcase->policy.opt = (enum funder_opt)(fromwire_u8(cursor, max) % 3);
tcase->policy.mod = fromwire_u64(cursor, max);
switch (tcase->policy.opt)
{
case MATCH:
tcase->policy.mod %= 201;
break;
case AVAILABLE:
tcase->policy.mod %= 101;
break;
case FIXED:
tcase->policy.mod %= MAX_SATS + 1;
break;
default:
assert(false && "invalid policy");
}
tcase->policy.min_their_funding = fromwire_amount_sat_bounded(cursor, max);
tcase->policy.max_their_funding = fromwire_amount_sat_bounded(cursor, max);
if (amount_sat_greater(tcase->policy.min_their_funding, tcase->policy.max_their_funding)) {
struct amount_sat tmp = tcase->policy.min_their_funding;
tcase->policy.min_their_funding = tcase->policy.max_their_funding;
tcase->policy.max_their_funding = tmp;
}
tcase->policy.per_channel_max = fromwire_amount_sat_bounded(cursor, max);
tcase->policy.per_channel_min = fromwire_amount_sat_bounded(cursor, max);
if (amount_sat_greater(tcase->policy.per_channel_min, tcase->policy.per_channel_max)) {
struct amount_sat tmp = tcase->policy.per_channel_min;
tcase->policy.per_channel_min = tcase->policy.per_channel_max;
tcase->policy.per_channel_max = tmp;
}
tcase->policy.fuzz_factor = fromwire_u8(cursor, max) % 101;
tcase->policy.reserve_tank = fromwire_amount_sat_bounded(cursor, max);
tcase->policy.fund_probability = fromwire_u8(cursor, max) % 101;
tcase->policy.leases_only = fromwire_u8(cursor, max) & 1;
return tcase;
}
void init(int *argc, char ***argv)
{}
void run(const u8 *data, size_t size)
{
struct test_case *tcase = new_test_case(tmpctx, &data, &size);
struct node_id id;
memset(&id, 1, sizeof(id));
const char *err;
struct amount_sat our_funds;
/* Call the function under test */
err = calculate_our_funding(&tcase->policy, id,
tcase->their_funds,
tcase->our_last_funds,
tcase->available_funds,
tcase->max_channel_size,
tcase->lease_request,
&our_funds);
/* Validate invariants */
if (!err)
{
/* Check total doesn't exceed max_channel_size */
struct amount_sat total;
if (!amount_sat_add(&total, tcase->their_funds, our_funds)) {
fprintf(stderr, "Overflow in total channel capacity\n");
abort();
}
if (amount_sat_greater(total, tcase->max_channel_size)) {
fprintf(stderr, "Total channel capacity %"PRIu64" exceeds size %"PRIu64"\n",
total.satoshis, tcase->max_channel_size.satoshis); /* Raw: fuzzing */
abort();
}
/* Check our_funds is within per-channel limits */
if (amount_sat_less(our_funds, tcase->policy.per_channel_min) &&
!amount_sat_is_zero(our_funds)) {
fprintf(stderr, "our_funds %"PRIu64" < per_channel_min %"PRIu64"\n",
our_funds.satoshis, tcase->policy.per_channel_min.satoshis); /* Raw: fuzzing */
abort();
}
if (amount_sat_greater(our_funds, tcase->policy.per_channel_max)) {
fprintf(stderr, "our_funds %"PRIu64" > per_max_channel_size %"PRIu64"\n",
our_funds.satoshis, tcase->policy.per_channel_max.satoshis); /* Raw: fuzzing */
abort();
}
}
/* Check available funds constraint */
struct amount_sat available_minus_reserve;
if (amount_sat_sub(&available_minus_reserve, tcase->available_funds, tcase->policy.reserve_tank)) {
if (amount_sat_greater(our_funds, available_minus_reserve)) {
fprintf(stderr, "our_funds %"PRIu64" > available %"PRIu64" - reserve %"PRIu64"\n",
our_funds.satoshis, tcase->available_funds.satoshis, /* Raw: fuzzing */
tcase->policy.reserve_tank.satoshis); /* Raw: fuzzing */
abort();
}
} else if (!amount_sat_eq(our_funds, AMOUNT_SAT(0))) {
fprintf(stderr, "Reserve %"PRIu64" >= available %"PRIu64" but our_funds %"PRIu64" != 0\n",
tcase->policy.reserve_tank.satoshis, tcase->available_funds.satoshis, /* Raw: fuzzing */
our_funds.satoshis); /* Raw: fuzzing */
abort();
}
clean_tmpctx();
}